review 结果
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
# Fonrey API 契约规范(API_CONTRACT)
|
||||
|
||||
**版本**: 1.1
|
||||
**适用范围**: 全模块(account / permission / property / client / complex / org / setting)
|
||||
**版本**: 1.2
|
||||
**适用范围**: 全模块(account / permission / property / client / complex / org / setting)+ 平台管理后台(admin)
|
||||
**关联总纲**: `TECH_STACK/TECH_STACK.md`
|
||||
**最后更新**: 2026-04-28
|
||||
**最后更新**: 2026-05-02
|
||||
|
||||
---
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
| 日期 | 变更人 | 变更内容 |
|
||||
|---|---|---|
|
||||
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
|
||||
| 2026-04-30 | Atlas | 补充"变更历史"章节(文档治理) |
|
||||
| 2026-05-02 | Atlas | 升至 v1.2:新增 §12 平台管理后台端点附录(A-B-1);适用范围扩展至 admin 模块 |
|
||||
|
||||
## 1. 文档定位与原则
|
||||
|
||||
@@ -543,3 +544,109 @@ THEN 两侧完全一致(无多余值,无缺失值,大小写相同)
|
||||
| **P1 注解补全** | 所有现有视图加 `@extend_schema`,枚举字段用 ChoiceField | `openapi.json` 无 `{}` 空 Schema;所有端点有 `summary` 和 `tags` |
|
||||
| **P2 契约测试** | schemathesis 纳入 CI,Positive 用例全绿 | CI status_code + response_schema 两项检查全 PASS |
|
||||
| **P3 持续守护** | openapi.json diff 纳入 PR Review;枚举值变更同步 enums.py | PR checklist 自动提醒 Schema 变更 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 平台管理后台端点附录(A-B-1 回应)
|
||||
|
||||
> **背景**:审核报告 A-B-1 指出 API_CONTRACT 缺少 `/admin/...` 命名空间端点规范,导致 Backend 实现缺乏契约约束。本章补齐平台管理后台(`admin.fonrey.com`)所有端点清单,作为机器可读约束附录。
|
||||
>
|
||||
> 适用规则:本章端点遵循第 2–10 章全局规范;以下特例优先级高于全局规范:
|
||||
> - 认证:Django Session(`adminSessionId` Cookie)+ CSRF(`X-CSRFToken`)+ 必要时 MFA Step-up;**不使用 JWT / Bearer Token**。
|
||||
> - 响应类型:`GET` 页面视图返回 `text/html`;`POST`/`PATCH` 写操作返回 JSON 或 HTMX partial(在端点备注中明示)。
|
||||
> - 版本控制:`/admin/...` 无版本号前缀;破坏性变更通过 Django View Mixin 灰度。
|
||||
> - 非法权限访问:统一返回 `403`(不暴露资源是否存在)。
|
||||
|
||||
### 12.1 认证端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求 | 成功响应 | 失败响应 | 备注 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `/admin/login/` | GET | 公开 | — | 200 HTML(登录表单) | — | 已登录时 302 → `/admin/` |
|
||||
| `/admin/login/` | POST | 公开 | `username`, `password` | 302 → `/admin/mfa/verify/` | 200 HTML(表单 + 错误) | 失败计数写 Redis;5 次锁 15 min |
|
||||
| `/admin/login/mfa/verify/` | GET | session 中有 `pre_mfa_admin_id` | — | 200 HTML(TOTP 输入框) | 302 → `/admin/login/` | 超时 5 min 未提交则清 pre_mfa |
|
||||
| `/admin/login/mfa/verify/` | POST | session 中有 `pre_mfa_admin_id` | `otp_code` | 302 → `/admin/` | 200 HTML(错误)/ 429(OTP 失败 ≥3) | 成功后写 `admin_sessions`,清 `pre_mfa_admin_id` |
|
||||
| `/admin/login/mfa/step-up/` | POST | AdminSession | `otp_code` | 200 JSON `{"ok": true}` | 400 JSON `{"error": "invalid_otp"}` | 写 `session.mfa_confirmed_at` + Redis `pub:mfa:stepup:*` |
|
||||
| `/admin/logout/` | POST | AdminSession | — | 302 → `/admin/login/` | — | 清 `admin_sessions`,flush session |
|
||||
|
||||
### 12.2 租户管理端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 失败响应 | 备注 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `/admin/tenants/` | GET | AdminSession(任意角色) | `?status=`, `?q=`, `?page_cursor=` | 200 HTML(租户列表页)| — | Keyset 分页;禁止 OFFSET |
|
||||
| `/admin/tenants/create/` | GET | AdminSession(SUPER/OPS) | — | 200 HTML(创建表单) | — | — |
|
||||
| `/admin/tenants/create/` | POST | AdminSession(SUPER/OPS)+ CSRF | `tenant_name`, `contact_phone`, `contact_email`, `license_user_limit`, `paid_until` | 302 → `/admin/tenants/<id>/` | 200 HTML(表单 + 错误)/ 409(tenant_code 冲突) | 写 `tenants`(`status='provisioning'`)后入队 `provision_tenant` Saga |
|
||||
| `/admin/tenants/<uuid:id>/` | GET | AdminSession | — | 200 HTML(详情页) | 404 | — |
|
||||
| `/admin/tenants/<uuid:id>/edit/` | GET | AdminSession(SUPER/OPS) | — | 200 HTML(编辑表单) | 404 | — |
|
||||
| `/admin/tenants/<uuid:id>/edit/` | POST | AdminSession(SUPER/OPS)+ CSRF | 可编辑字段(name / contact_* / license_user_limit / paid_until) | 302 → 详情页 | 200 HTML(表单 + 错误)| 写 `platform_audit_logs`(`UPDATE_TENANT_INFO`)|
|
||||
| `/admin/tenants/<uuid:id>/suspend/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | `suspended_reason`, `suspended_until`(可选) | 200 JSON `{"ok": true}` + HTMX partial | 400 / 409(已暂停)| `suspended_reason` 枚举见 `ENUMS.md`;写审计 |
|
||||
| `/admin/tenants/<uuid:id>/resume/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | — | 200 JSON `{"ok": true}` + HTMX partial | 409(非 suspended 状态)| 写审计 |
|
||||
| `/admin/tenants/<uuid:id>/cancel/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | `reason` | 200 JSON `{"ok": true}` | 409(非 active 状态)| 进入 `pending_cancellation`;冷静期 30 天 |
|
||||
| `/admin/tenants/<uuid:id>/hard-delete/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up(5 min 内)+ 双人复核 token | `confirm_tenant_name`, `reviewer_token` | 200 JSON `{"ok": true}` | 400(名称不符)/ 403(token 无效)/ 409(非 pending_cancellation + 30d 状态)| 入队 `hard_delete_tenant`;写审计 `HARD_DELETE_TENANT` |
|
||||
| `/admin/tenants/<uuid:id>/update-license/` | POST | AdminSession(SUPER/OPS)+ CSRF | `paid_until`, `license_user_limit` | 200 JSON `{"ok": true}` | 400(参数非法)| 写审计 `UPDATE_LICENSE` / `UPDATE_LICENSE_USER_LIMIT` |
|
||||
| `/admin/tenants/<uuid:id>/export/` | POST | AdminSession(SUPER/OPS)+ CSRF | `export_format`(`csv`/`json`)| 202 JSON `{"task_id": "..."}` | — | 入队 `run_export`;写 `export_tasks`(`status='pending'`) |
|
||||
| `/admin/tenants/<uuid:id>/export/<uuid:task_id>/status/` | GET | AdminSession | — | 200 JSON `{"status": "pending"\|"in_progress"\|"success"\|"failed", "download_url": null\|"..."}` | 404 | HTMX 轮询端点;终态时移除 `hx-trigger` |
|
||||
|
||||
### 12.3 平台管理员账号端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 备注 |
|
||||
|---|---|---|---|---|---|
|
||||
| `/admin/admins/` | GET | AdminSession(SUPER) | — | 200 HTML(管理员列表) | — |
|
||||
| `/admin/admins/create/` | POST | AdminSession(SUPER)+ CSRF | `username`, `email`, `role` | 302 → 列表页 | 写审计 `CREATE_PLATFORM_ADMIN` |
|
||||
| `/admin/admins/<uuid:id>/edit/` | POST | AdminSession(SUPER)+ CSRF | `role`, `is_active` | 302 → 列表页 | 写审计 `UPDATE_PLATFORM_ADMIN_ROLE` |
|
||||
| `/admin/admins/<uuid:id>/deactivate/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | — | 302 → 列表页 | 清对应 `admin_sessions`;写审计 `DEACTIVATE_PLATFORM_ADMIN` |
|
||||
| `/admin/admins/<uuid:id>/reset-password/` | POST | AdminSession(SUPER)+ CSRF | — | 200 JSON `{"ok": true}` | 发初始密码邮件;写审计 |
|
||||
|
||||
### 12.4 IP 白名单端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 备注 |
|
||||
|---|---|---|---|---|---|
|
||||
| `/admin/ip-whitelist/` | GET | AdminSession(SUPER) | — | 200 HTML(白名单列表) | — |
|
||||
| `/admin/ip-whitelist/add/` | POST | AdminSession(SUPER)+ CSRF | `cidr`, `label`, `expires_at`(可选)| 302 → 列表页 | 清 Redis `pub:ipwl:active` |
|
||||
| `/admin/ip-whitelist/<uuid:id>/toggle/` | POST | AdminSession(SUPER)+ CSRF | `is_active` | 200 JSON `{"ok": true}` | 清 Redis `pub:ipwl:active` |
|
||||
| `/admin/ip-whitelist/<uuid:id>/delete/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | — | 302 → 列表页 | 清 Redis `pub:ipwl:active`;写审计 |
|
||||
|
||||
### 12.5 客户端版本发布端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 备注 |
|
||||
|---|---|---|---|---|---|
|
||||
| `/admin/releases/` | GET | AdminSession | — | 200 HTML(版本列表) | — |
|
||||
| `/admin/api/client-releases/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | `version`, `platform`, `arch`, `release_type`, `artifact`(文件上传)| 201 JSON(版本对象)| 校验 SemVer;入队 checksum + scan |
|
||||
| `/admin/api/client-releases/<uuid:pk>/` | PATCH | AdminSession(SUPER)+ CSRF | `status`, `release_notes`, `force_update`, `min_required_version` | 200 JSON(更新后对象)| 状态机约束见 `平台管理后台技术方案.md §5.2` |
|
||||
| `/admin/api/client-releases/<uuid:pk>/rollback/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up | — | 200 JSON `{"ok": true}` | 原子切换 published;写审计 `ROLLBACK_CLIENT_RELEASE` |
|
||||
|
||||
### 12.6 备份与恢复端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 备注 |
|
||||
|---|---|---|---|---|---|
|
||||
| `/admin/backups/` | GET | AdminSession(SUPER/OPS) | — | 200 HTML(备份列表) | — |
|
||||
| `/admin/backups/trigger/` | POST | AdminSession(SUPER)+ CSRF | `backup_type`(`full`/`incremental`)| 202 JSON `{"task_id": "..."}` | 入队 `run_backup` |
|
||||
| `/admin/backups/<uuid:id>/status/` | GET | AdminSession | — | 200 JSON `{"status": "...", "size_bytes": ...}` | HTMX 轮询端点 |
|
||||
| `/admin/backups/<uuid:id>/restore/` | POST | AdminSession(SUPER)+ CSRF + MFA Step-up(5 min 内)+ 双人复核 token | `confirm_text`, `reviewer_token` | 202 JSON `{"task_id": "..."}` | 高危操作;入队 `run_restore`;写审计 `RESTORE_BACKUP` |
|
||||
|
||||
### 12.7 平台审计日志端点
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 请求要点 | 成功响应 | 备注 |
|
||||
|---|---|---|---|---|---|
|
||||
| `/admin/audit-logs/` | GET | AdminSession(SUPER/OPS) | `?action_type=`, `?operator_id=`, `?target_id=`, `?from=`, `?to=`, `?page_cursor=` | 200 HTML(审计列表)| Keyset 分页;只读,无写操作 |
|
||||
| `/admin/audit-logs/<uuid:id>/` | GET | AdminSession(SUPER/OPS) | — | 200 HTML(详情)| — |
|
||||
|
||||
### 12.8 仪表盘端点(统计只读)
|
||||
|
||||
| 端点 | 方法 | 鉴权 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `/admin/dashboard/` | GET | AdminSession(任意角色) | 总览页(读 Redis 缓存) |
|
||||
| `/admin/dashboard/stats/` | GET | AdminSession | HTMX 局刷统计块;读 `pub:dashboard:stats` 缓存 |
|
||||
| `/admin/dashboard/version-distribution/` | GET | AdminSession | 版本分布图;转发至 `/api/release/v1/metrics/version-distribution/` |
|
||||
|
||||
### 12.9 端点完整性守护规则
|
||||
|
||||
> 本节端点清单必须与 `平台管理后台技术方案.md §4` 保持同步。新增端点必须同时更新本表和技术方案;删除/重命名端点必须先创建 ADR,格式 `ADR-YYYYMMDD-XXX`,本文件"变更历史"行追加记录。
|
||||
|
||||
AI agent 实现时验证词:
|
||||
|
||||
```
|
||||
GIVEN 本附录第 12.1–12.8 节的端点清单
|
||||
WHEN 开发者实现或修改任意 /admin/... 端点
|
||||
THEN 端点鉴权方式、请求字段、成功状态码、失败状态码与本附录完全一致;
|
||||
任何缺失端点视为 A-B-1 级 Blocker
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user