108 lines
5.7 KiB
Markdown
108 lines
5.7 KiB
Markdown
# api-server 主工程 crate 占位说明
|
||
|
||
日期:`2026-04-20`
|
||
|
||
## 1. crate 职责
|
||
|
||
`api-server` 是新后端的 Axum 主工程 crate,后续负责:
|
||
|
||
1. `main.rs` 启动入口
|
||
2. `Router` 装配
|
||
3. `with_state` 共享状态注入
|
||
4. 中间件挂载
|
||
5. `/healthz`、`/api/*`、SSE 与静态资源兼容层装配
|
||
6. 由 `../../scripts/dev.ps1` 与 `../../scripts/dev.sh` 驱动的本地开发启动链路
|
||
7. 由 `../../scripts/test.ps1` 与 `../../scripts/test.sh` 驱动的本地测试链路
|
||
8. 由 `../../scripts/check.ps1` 与 `../../scripts/check.sh` 驱动的本地统一检查链路
|
||
9. 由 `../../scripts/smoke.ps1` 与 `../../scripts/smoke.sh` 驱动的本地启动与协议冒烟链路
|
||
|
||
## 2. 当前阶段说明
|
||
|
||
当前目录已经完成以下基础骨架:
|
||
|
||
1. 目录占位
|
||
2. `Cargo.toml`
|
||
3. `src/main.rs`
|
||
4. `src/app.rs`
|
||
5. `src/state.rs`
|
||
6. `src/config.rs`
|
||
7. 基础 `TraceLayer` 挂载
|
||
8. 接入 `shared-logging` 完成 `tracing subscriber` 初始化
|
||
9. 接入 `POST /api/auth/entry` 首版密码登录链路
|
||
10. 接入 `POST /api/assets/direct-upload-tickets` 直传票据接口
|
||
11. 接入 `GET /api/auth/me` 当前用户查询链路
|
||
12. 接入 `POST /api/auth/refresh` refresh token 轮换链路
|
||
|
||
后续与本 crate 直接相关的任务包括:
|
||
|
||
1. [x] 接入统一日志与 tracing
|
||
2. [x] 接入 `request_id`
|
||
3. [x] 接入统一错误处理中间件
|
||
4. [x] 接入 response envelope
|
||
5. [x] 接入 `/healthz`
|
||
6. [x] 接入 `/api/auth/entry`
|
||
7. [x] 接入 `/api/assets/direct-upload-tickets`
|
||
8. [x] 接入 `/api/auth/me`
|
||
9. [x] 接入 `/api/auth/refresh`
|
||
|
||
当前 tracing 约定:
|
||
|
||
1. 进程启动时通过 `shared-logging` 统一初始化 `tracing subscriber`。
|
||
2. 默认日志过滤器来自 `GENARRATIVE_API_LOG`,未提供时回落到 `info,tower_http=info`。
|
||
3. HTTP 访问日志统一通过 Axum 路由层的 `TraceLayer` 输出,后续 `request_id`、响应头与错误中间件继续在同一层扩展。
|
||
|
||
当前 request context 约定:
|
||
|
||
1. 中间件优先读取来访 `x-request-id`,未提供时生成新的 UUID。
|
||
2. `request_id` 会统一写入请求 `extensions` 与请求头,供 tracing、错误处理中间件和响应头层复用。
|
||
3. 最终响应会回写同一个 `x-request-id`,保证调用方、日志链路和后续 envelope `meta.requestId` 可对齐。
|
||
|
||
当前错误处理中间件约定:
|
||
|
||
1. 对 Axum 默认产生的空 `4xx / 5xx` 响应,统一归一化为 legacy 兼容 JSON 错误体:`{ error: { code, message, details? } }`。
|
||
2. 已经带 `content-type` 的业务错误响应不会被覆盖,避免抢走后续 response envelope 的职责。
|
||
3. 统一错误日志会复用当前请求的 `request_id`,便于后续和响应头、envelope 元信息串联。
|
||
|
||
当前 response envelope 约定:
|
||
|
||
1. `RequestContext` 已记录 `request_id`、请求开始时间、默认 `operation` 与 envelope 协商结果。
|
||
2. `json_success_body(...)` / `json_error_body(...)` 会根据 `x-genarrative-response-envelope` 自动在“裸数据 / 标准 envelope / legacy error + meta”之间切换。
|
||
3. `meta.apiVersion`、`meta.requestId`、`meta.routeVersion`、`meta.operation`、`meta.latencyMs`、`meta.timestamp` 已按当前前端契约生成,响应头回写仍留给后续独立任务。
|
||
|
||
当前基础响应头约定:
|
||
|
||
1. 所有响应都会回写 `x-request-id`。
|
||
2. 所有响应都会回写固定的 `x-api-version`,当前值与 body `meta.apiVersion` 保持一致。
|
||
3. 所有响应都会回写 `x-route-version`,当前阶段默认与 `x-api-version` 保持一致,后续再按路由粒度细分。
|
||
4. 所有响应都会回写 `x-response-time-ms`,值来源于 `RequestContext` 内记录的请求开始时间。
|
||
|
||
当前 `/healthz` 约定:
|
||
|
||
1. 路径固定为 `/healthz`。
|
||
2. 裸响应继续返回 `{ ok: true, service: "genarrative-node-server" }`,保持与当前 Node 工程兼容。
|
||
3. 当请求携带 `x-genarrative-response-envelope` 时,`/healthz` 会返回标准 success envelope。
|
||
4. `x-request-id`、`x-api-version`、`x-route-version`、`x-response-time-ms` 会在 `/healthz` 响应中一并回写。
|
||
|
||
当前本地检查链路约定:
|
||
|
||
1. `../../scripts/check.ps1` 与 `../../scripts/check.sh` 统一串联 `cargo fmt --all --check`、`cargo clippy`、`cargo check`、`cargo test`。
|
||
2. 默认检查整个 `server-rs` workspace,确保后续多 crate 扩容时仍然保持统一口径。
|
||
3. 当只需聚焦单个 crate 时,可通过 `-Package` 或 `SERVER_RS_CHECK_PACKAGE` 收窄 `clippy / check / test` 目标。
|
||
4. `cargo fmt --all --check` 仍固定覆盖整个 workspace,避免多 crate 下格式基线漂移。
|
||
|
||
当前本地 smoke 链路约定:
|
||
|
||
1. `../../scripts/smoke.ps1` 与 `../../scripts/smoke.sh` 会先构建 `api-server`,再拉起临时本地进程完成冒烟验证。
|
||
2. smoke 当前固定校验 `/healthz` 的 raw 响应、envelope 响应以及 `x-request-id`、`x-api-version`、`x-route-version`、`x-response-time-ms` 头。
|
||
3. smoke 通过后,可作为“Axum 服务可独立启动且基础 contract 可联通”的本地自动化证据。
|
||
|
||
## 3. 边界约束
|
||
|
||
1. `api-server` 负责 HTTP、SSE、Cookie、Header、路由与协议装配。
|
||
2. 业务逻辑优先通过独立模块 crate 暴露能力,再由主工程组合。
|
||
3. 外部副作用通过 `platform-auth`、`platform-oss`、`platform-llm` 与各模块 crate 的应用层完成。
|
||
4. 不把领域规则直接堆在 handler 中。
|
||
5. 当前密码登录由 `module-auth` 负责用例编排,`api-server` 只负责请求解析、JWT 签发与 refresh cookie 写回。
|
||
6. 当前 `/api/auth/me` 复用现有 Bearer JWT 中间件与 `module-auth` 用户快照查询,不直接绕过模块边界读取内部状态。
|
||
7. 当前 `/api/auth/refresh` 复用 `module-auth` 的 refresh session 轮换能力,`api-server` 负责 refresh cookie 读取、失败清理与 access token 重签。
|