Files
craftlabs-authorization-sdk/services/RUNBOOK.md
T
huangping d53ddf32c8 feat(i8-i9): webhook DEAD replay, read-only delivery status, and callback UI
I8: platform proxies replay to webhook; webhook ops token filter and internal
replay endpoint; delivery service supports read/replay flows.

I9: platform GET callback webhook delivery status by inbox id; UI shows
read-only status block and handles load errors without blocking the page.

Also refresh OpenAPI, Runbook notes, test fixtures and YAML; fix Vite dev
axios baseURL so /api uses proxy; improve login error messaging.

Made-with: Cursor
2026-04-07 21:26:44 +08:00

151 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 平台后端部署 Runbook(单机 Fat JAR
面向 **广州创飞** 小团队:**单/双进程 `java -jar`** + **PostgreSQL 15**,无 K8s 前提。
## 1. 组件与端口
| 进程 | JAR | 默认端口 | 说明 |
|------|-----|----------|------|
| 交付平台 API | `delivery-platform-api-*-SNAPSHOT.jar` | **8080** | JWT、M1+ 领域 API、Flyway 表 **`flyway_platform_api`** |
| License Webhook | `license-webhook-ingress-*-SNAPSHOT.jar` | **8081** | 比特 CallbackFlyway 表 **`flyway_webhook`** |
两进程可部署在 **同一主机****两台主机**;共用同一数据库实例时,须使用 **不同 Flyway 历史表**(已在 `application.yml` 配置)。
## 2. 构建产物
```bash
mvn -f services/pom.xml -pl delivery-platform-api -am clean package
mvn -f services/pom.xml -pl license-webhook-ingress -am clean package
```
产物:`services/*/target/*.jar`Spring Boot repackage)。
## 3. PostgreSQL 15
本地/联调示例:
```bash
docker compose -f services/docker-compose.yml up -d
```
默认库 `craftlabs_platform`、用户/密码 `craftlabs`/`craftlabs`**生产须替换**)。
**网络**:数据库 **不对公网**;仅应用主机或 VPC 内可达。
## 4. 环境变量(摘要)
### 共用数据源(两服务均可)
| 变量 | 说明 |
|------|------|
| `SPRING_DATASOURCE_URL` | 例:`jdbc:postgresql://db:5432/craftlabs_platform` |
| `SPRING_DATASOURCE_USERNAME` | 数据库用户 |
| `SPRING_DATASOURCE_PASSWORD` | 数据库密码 |
### 平台 API 专有
| 变量 | 说明 |
|------|------|
| `PLATFORM_JWT_SECRET` | **必填(生产)**,长度 ≥ **32** 字符;用于签发/校验 JWT |
| `PLATFORM_JWT_EXPIRY_SECONDS` | 可选,默认 `43200`12h |
### Webhook 专有
| 变量 | 说明 |
|------|------|
| `CRAFTLABS_WEBHOOK_EXPECTED_TOKEN` | **生产强烈建议设置**;与请求头 `x-bitanswer-token` 一致 |
## 5. 启动顺序与迁移
- **Flyway**:各 JAR **首次启动** 时自动迁移;无强制先后,但 **同一库** 上两应用使用 **不同历史表**,互不覆盖。
- **建议**:先确认数据库已创建且账号可连,再先后启动 **API****Webhook**(顺序可互换)。
示例(生产请用 systemd/supervisor 等托管):
```bash
export SPRING_DATASOURCE_URL=jdbc:postgresql://127.0.0.1:5432/craftlabs_platform
export SPRING_DATASOURCE_USERNAME=craftlabs
export SPRING_DATASOURCE_PASSWORD='********'
export PLATFORM_JWT_SECRET='至少32字符的随机密钥'
java -jar delivery-platform-api-0.1.0-SNAPSHOT.jar
# 另一终端
export CRAFTLABS_WEBHOOK_EXPECTED_TOKEN='与比特控制台配置一致'
java -jar license-webhook-ingress-0.1.0-SNAPSHOT.jar
```
## 6. 健康检查
- API`GET http://127.0.0.1:8080/actuator/health`
- Webhook`GET http://127.0.0.1:8081/actuator/health`
## 7. 契约与前端
- OpenAPI 快照:`contracts/openapi/delivery-platform-api.json`
- 运行时文档:`http://<api-host>:8080/swagger-ui.html`(生产建议 **限制 IP****关闭**
前端静态资源由 **Nginx/Caddy** 托管,`/api` **反代** 至 8080;参见 `web/delivery-platform-ui` README / `vite` 开发代理配置。
## 8. 禁止事项(架构)
- 平台 **classpath 禁止** `craftlabs-auth-bitanswer`Maven Enforcer + CI 门禁);详见根目录 `contracts/README.md``services/pom.xml` 说明。
## 9. 回滚
- 应用:回退上一版 JAR 并重启。
- 数据库:Flyway **无自动 down**;回滚需 **人工迁移脚本** 或从备份恢复(生产变更前应备份)。
## 10. I5/I6:内部 Token、Webhook → 平台与轮换
### 10.1 变量对照
| 组件 | 变量 / 配置键 | 说明 |
|------|----------------|------|
| **平台** `delivery-platform-api` | `PLATFORM_INTERNAL_TOKEN``CRAFTLABS_PLATFORM_INTERNAL_TOKEN``platform.internal.token` | 校验入站 `X-Platform-Internal-Token` |
| **Webhook** `license-webhook-ingress` | `CRAFTLABS_PLATFORM_INTERNAL_TOKEN``craftlabs.platform.internal.token` | 出站请求头,**须与平台一致** |
| **Webhook** | `PLATFORM_INTERNAL_BASE_URL``craftlabs.platform.internal.base-url` | 平台根 URL,无尾斜杠;转发 `POST …/internal/v1/callback-events` |
未配置 `base-url` 或 token 时,Webhook **仅落库收据**,不向平台投递(本地可只验 bitanswer token)。
### 10.2 连通性自检(平台已启动)
```bash
# 将 <TOKEN> 与平台环境变量一致
curl -sS -o /dev/null -w "%{http_code}\n" \
-X POST "http://127.0.0.1:8080/internal/v1/callback-events" \
-H "Content-Type: application/json" \
-H "X-Platform-Internal-Token: <TOKEN>" \
-H "Idempotency-Key: runbook-probe-$(date +%s)" \
-d '{"schemaVersion":"1.0","sourceSystem":"BITANSWER","externalMessageId":"runbook-probe-msg","eventType":"probe","rawPayload":{}}'
```
期望:**401** 表示路由可达但 Token 错误;**400** 可能为体缺字段;**200** 且 JSON 含 `inboxId` 表示鉴权与入队逻辑正常。
### 10.3 Token 轮换(简要)
1. 在平台与 Webhook **同时**配置新 Token(滚动窗口内可同时接受旧+新需改代码时另议;**MVP 为单值**,应选维护窗**同步切换**)。
2. 重启 **平台** API,再重启 **Webhook**(避免 Webhook 仍用旧值调用已切换的平台)。
3.**§10.2** 探测;再在 UI **Callback 收件箱** 验证新事件可见。
4. 审计:记录轮换时间与操作人(可另走工单)。
### 10.4 Flyway 历史表
- 平台:`flyway_platform_api`(迁移含 `platform_callback_inbox`、M6 表等)。
- Webhook`flyway_webhook``V1` 收据表、`V2` **`webhook_platform_delivery`** 平台投递出库)。**同一 PostgreSQL 实例** 下两表共存,**勿**手动改名或合并。
### 10.5 I7:异步投递与调度(Webhook
| 配置 | 说明 |
|------|------|
| `craftlabs.platform.delivery.scheduler-enabled` | 默认 `true`;单测/特殊场景可 `false` |
| `craftlabs.platform.delivery.tick-ms` | 调度间隔(毫秒) |
| `craftlabs.platform.delivery.max-attempts` | 单条投递最大尝试次数,超限标记 **`DEAD`** |
| `craftlabs.platform.delivery.batch-size` | 每 tick 最多拉取条数 |
比特 Callback **2xx** 在收据落库与 **出站行入队** 之后返回;真正 `POST` 平台由后台线程执行。`DEAD` 行需人工依据 `last_error` 与平台侧幂等处理。
**I8 — DEAD 重放入队**:在平台与 Webhook 配置 **`LICENSE_WEBHOOK_BASE_URL`**Webhook 根 URL)与 **`LICENSE_WEBHOOK_OPS_TOKEN`**(两侧相同;保护 Webhook `POST /internal/v1/platform-deliveries/by-receipt/{receiptId}/replay`)。**OPS / SYS_ADMIN** 可在 UI **Callback 详情** 触发「重新入队出库」,平台会按收件箱的 `webhookReceiptId` 代调 Webhook;仅当出库行为 **`DEAD`** 时成功。亦可手工:`curl -X POST -H "X-Webhook-Ops-Token: …" "http://<webhook>/internal/v1/platform-deliveries/by-receipt/<receiptId>/replay"`
**I9 — 出库状态只读**:平台 `GET /api/v1/callback-inbox/{id}/webhook-delivery` 代调 Webhook `GET …/by-receipt/{receiptId}`(同一 Ops Token);UI 详情展示 `status` / `attempts` / `lastError` 等。