26 KiB
For AI assistants: Read this entire file before designing or implementing any API. Contract rules here are mandatory. Do not invent per-module variants unless explicitly allowed.
Fonrey API 契约规范(API_CONTRACT)
版本: 1.2
适用范围: 全模块(account / permission / property / client / complex / org / setting)+ 平台管理后台(admin)
关联总纲: TECH_STACK/TECH_STACK.md
最后更新: 2026-05-02
变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充"变更历史"章节(文档治理) |
| 2026-05-02 | Atlas | 升至 v1.2:新增 §12 平台管理后台端点附录(A-B-1);适用范围扩展至 admin 模块 |
1. 文档定位与原则
本文件定义 Fonrey 全局 API 契约标准,解决跨模块接口风格漂移问题。模块技术方案中的 API 章节必须遵循本文件,不得各自定义冲突规则。
1.1 强制级别
- MUST:必须遵守,违反视为缺陷
- SHOULD:建议遵守,若不遵守需在模块文档注明原因
- MAY:可选能力
1.2 适用接口类型
- JSON API(
/api/**) - HTMX 片段端点(HTML response)
- 文件上传/下载端点
2. 请求 / 响应格式规范
2.1 请求体规范(JSON API)
Content-TypeMUST 为application/jsoncharset=utf-8SHOULD 显式声明- 写操作(POST/PUT/PATCH)MUST 传业务 payload;禁止空对象写入
推荐请求结构:
{
"data": {
"...": "业务字段"
},
"meta": {
"request_id": "可选,客户端透传"
}
}
兼容说明:历史端点已存在 filters/sort/pagination 平铺结构时可继续使用,但新接口 SHOULD 迁移到 data 容器。
2.2 成功响应规范(JSON API)
成功响应 MUST 使用统一 envelope:
{
"ok": true,
"data": {},
"meta": {
"request_id": "uuid",
"timestamp": "2026-04-27T16:30:00+08:00"
}
}
说明:
okMUST 为truedataMUST 存在(可为空对象{}或空数组[])metaSHOULD 包含request_id与服务端时间
2.3 失败响应规范(JSON API)
失败响应 MUST 使用统一 envelope:
{
"ok": false,
"error": "权限不足",
"code": "PROPERTY_PERMISSION_DENIED",
"details": {},
"meta": {
"request_id": "uuid",
"timestamp": "2026-04-27T16:30:00+08:00"
}
}
说明:
errorMUST 为面向用户/调用方可读消息codeMUST 为稳定机器可读码(大写下划线)detailsMAY 提供字段级错误(如校验失败)
2.4 HTMX 响应规范
- 成功:返回 HTML 片段;必要时通过
HX-Trigger触发前端事件 - 失败:
- 状态码 MUST 正确(4xx/5xx)
- SHOULD 在响应头返回
HX-Trigger,例如:{"toast:error":"权限不足"}{"toast:error":"请求失败,请重试"}
3. 错误码规范
3.1 命名规则
- 错误码 MUST 为
UPPER_SNAKE_CASE - 推荐前缀:
<MODULE>_(如PROPERTY_/CLIENT_/ORG_)
3.2 HTTP 状态码基线
| HTTP | 使用场景 | 示例 code |
|---|---|---|
| 400 | 参数错误、业务前置条件不满足 | PROPERTY_VALIDATION_ERROR |
| 401 | 仅用于纯 API Token 鉴权失败(当前 Web 会话模式一般不用) | AUTH_UNAUTHORIZED |
| 403 | 已登录但无权限 | *_PERMISSION_DENIED |
| 404 | 资源不存在或不可见 | *_NOT_FOUND |
| 409 | 状态冲突、任务未就绪 | *_STATE_CONFLICT / *_JOB_NOT_READY |
| 422 | 字段级校验错误(可选) | *_VALIDATION_FAILED |
| 429 | 频控触发 | RATE_LIMITED |
| 500 | 未预期异常 | INTERNAL_ERROR |
3.3 稳定性要求
codeMUST 可稳定依赖,不得频繁改名- 错误文案
error可优化,但不应影响调用方流程判断
4. 分页规范
Fonrey 列表查询 MUST 使用 Keyset 分页;禁止 OFFSET 深分页。
4.1 请求格式
{
"filters": {},
"sort": {"field": "updated_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}
4.2 响应格式
{
"ok": true,
"data": {
"items": [],
"next_cursor": "opaque_cursor_2"
},
"meta": {
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
}
}
4.3 约束
limitMUST 有上限(建议 ≤ 100)cursorMUST 为不透明字符串,禁止暴露内部排序字段组合- 排序字段 MUST 来自白名单,防止 SQL 注入与慢查询
5. 搜索 / 筛选规范
5.1 推荐请求结构
{
"filters": {
"keyword": "保利",
"status": ["active", "pending"],
"district_id": "uuid"
},
"sort": {"field": "updated_at", "order": "desc"},
"pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}
5.2 语义规范
keyword:模糊检索词(服务端统一做 trim)- 多选条件 MUST 使用数组(如
status: []) - 空数组
[]语义:不限制该条件 null语义:由模块文档明确(默认建议等同“不传”)
5.3 安全与性能
- 仅允许白名单字段参与筛选和排序
- LIKE/全文检索字段 SHOULD 建立索引或搜索策略
- 查询快照哈希(用于缓存/导出)SHOULD 对 filters+sort+scope 进行规范化后计算
6. 上传规范
Fonrey 优先采用“预签名上传 + 回执提交(commit)”两段式。
6.1 标准流程
- 客户端请求 upload-token(业务 API)
- 客户端直传对象存储(R2)
- 客户端调用 commit API 回写元数据
6.2 合约要求
- upload-token MUST 短时有效(建议 5~15 分钟)
- commit MUST 幂等(建议支持
idempotency_key) - 上传白名单与大小限制 MUST 在模块文档声明并在服务端校验
- SHOULD 校验
content_type与size - MAY 增加
sha256校验确保完整性
6.3 错误码建议
*_UPLOAD_TOKEN_EXPIRED(409/400)*_UPLOAD_FILE_TOO_LARGE(400)*_UPLOAD_FILE_TYPE_NOT_ALLOWED(400)*_UPLOAD_COMMIT_CONFLICT(409)
7. 文件下载规范
下载统一采用“导出任务 + 状态查询 + download endpoint”。
7.1 标准流程
- 创建导出任务
POST /api/**/export/jobs/ - 轮询任务状态
GET /api/**/export/jobs/{job_id}/ - 下载结果
GET /api/**/export/jobs/{job_id}/download/
7.2 合约要求
- 任务未完成下载 MUST 返回
409+*_EXPORT_JOB_NOT_READY - 下载链接 SHOULD 为一次性或短时有效 URL
- 响应 SHOULD 设置
Content-Disposition(附件下载) - 文件名 SHOULD 带模块与日期,便于审计
8. 权限拒绝返回规范
8.1 JSON API
- 未登录:MUST 返回
302(Web Session 场景)或401(纯 API 场景) - 已登录无权限:MUST 返回
403 - 失败体 MUST 使用统一错误 envelope,
code为*_PERMISSION_DENIED
8.2 页面路由(SSR/HTMX)
- 未登录:302 跳转登录页
- 已登录无权限:403 页面(或 HTMX 403 片段)
- HTMX 拒绝 SHOULD 触发
HX-Triggertoast 事件
8.3 测试强约束
每个受保护端点 MUST 覆盖三态:
- 200(有权限)
- 403(已登录无权限)
- 302/401(未登录,视端点类型)
9. 乐观锁(Optimistic Locking)规范
9.1 适用场景
properties、clients、complexes 三张高竞争表的更新操作(PUT/PATCH)MUST 使用乐观锁并发控制,防止"后写覆盖先写"数据丢失。
9.2 请求规范
客户端发起更新时,MUST 在请求体中携带当前资源版本号:
{
"data": {
"sale_price": 180,
"version": 3
}
}
兼容说明:当前 Fonrey 为内部 Web / Electron 客户端,采用请求体传递
version字段,无需If-MatchHeader(避免 HTMX 额外配置复杂度)。未来若提供对外开放 REST API,可补充支持If-Match: <version>Header 形式。
9.3 服务端执行规范
服务端执行 UPDATE 时 MUST 同时匹配 version,并将 version +1:
UPDATE properties
SET sale_price = :sale_price,
version = version + 1,
updated_at = NOW(),
updated_by = :operator_id
WHERE id = :id
AND version = :client_version -- 乐观锁匹配
AND deleted_at IS NULL;
- 若受影响行数 = 1:更新成功,返回
200 - 若受影响行数 = 0:抛
ConflictError,返回409+ code*_VERSION_CONFLICT
9.4 冲突响应规范
{
"ok": false,
"error": "已被他人修改,请刷新重试",
"code": "PROPERTY_VERSION_CONFLICT",
"details": {
"field": "version",
"your_version": 3,
"hint": "请重新获取最新数据后再提交"
},
"meta": {
"request_id": "uuid",
"timestamp": "2026-04-28T10:00:00+08:00"
}
}
- HTTP 状态码 MUST 为
409 code格式:<MODULE>_VERSION_CONFLICT(如PROPERTY_VERSION_CONFLICT/CLIENT_VERSION_CONFLICT/COMPLEX_VERSION_CONFLICT)- 前端 SHOULD 展示 Toast:「已被他人修改,请刷新重试」,并自动触发资源重新加载
9.5 Check List
version字段在 GET 响应中 MUST 返回(供后续 PUT/PATCH 携带)- 服务层 update 方法 MUST 校验受影响行数,0 行时抛
ConflictError - 前端表单 MUST 在隐藏域中保存
version,随 PUT/PATCH 提交 - HTMX 场景:冲突时后端 MUST 返回
HX-Trigger: {"toast:error":"已被他人修改,请刷新重试"} - 测试 MUST 覆盖:并发两次更新同版本,第二次 MUST 返回
409
10. 与模块文档的衔接规则
- 各模块技术方案中的“四、API 设计原则”“六、关键 API 规范”“十二、错误码建议”必须引用本文件
- 模块文档可补充模块特有 code 与字段,但不得与本规范冲突
- 冲突时以本文件为准;若需例外,必须在模块文档显式记录 ADR 链接
10. 落地检查清单(Review Checklist)
- 是否使用统一成功/失败 envelope
- 错误码是否为稳定
UPPER_SNAKE_CASE - 列表接口是否全部 Keyset 分页
- filters/sort 字段是否白名单化
- 上传是否采用 token+commit 且具备幂等保障
- 下载是否采用 job 流程并处理未就绪 409
- 权限拒绝是否遵循 200/403/302(401) 三态
- 测试是否覆盖契约关键路径
- 所有视图是否附加
@extend_schema(或@extend_schema_view)注解 - 枚举字段是否通过
OpenApiTypes或ChoiceField在 Schema 中完整暴露所有值 - 生成的
openapi.json是否已提交 / 与代码同步更新 schemathesis契约测试是否纳入 CI(至少覆盖 Positive 用例)
11. OpenAPI 落地规范(机器可读契约)
For AI assistants: §11 是实现层强约定。生成视图代码时必须同步写 @extend_schema;生成测试代码时必须包含契约断言。
11.1 工具链(MUST)
| 角色 | 工具 | 说明 |
|---|---|---|
| Schema 生成 | drf-spectacular |
唯一授权的 OpenAPI 生成库;禁止 drf-yasg |
| Schema 文件 | openapi.json(根目录) |
每次 CI 必须重新生成并 diff 检查 |
| 契约测试 | schemathesis |
基于生成 Schema 做 Positive + Negative 测试 |
| 文档 UI | Swagger UI(/api/docs/) |
开发环境默认开启,生产环境按需 |
安装:
pip install drf-spectacular schemathesis
settings.py 最低配置:
INSTALLED_APPS += ["drf_spectacular"]
SPECTACULAR_SETTINGS = {
"TITLE": "Fonrey API",
"VERSION": "1.1.0",
"SERVE_INCLUDE_SCHEMA": False,
"SCHEMA_PATH_PREFIX": r"/api/",
# 枚举值直接展开(不折叠成 $ref),便于 AI agent 直接读值
"ENUM_GENERATE_CHOICE_DESCRIPTION": True,
"COMPONENT_SPLIT_REQUEST": True,
}
urls.py:
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns += [
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/docs/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
]
11.2 视图注解规范(MUST)
每个 APIView / ViewSet 动作 MUST 携带 @extend_schema,最低包含:
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes
@extend_schema(
summary="获取房源详情", # 简短操作名(中文 OK)
tags=["property"], # 模块 tag,与路由前缀一致
responses={200: PropertyDetailSerializer},
# 失败响应也要声明,给 AI agent 提供完整错误路径
responses={
200: PropertyDetailSerializer,
403: OpenApiTypes.OBJECT, # 统一 error envelope
404: OpenApiTypes.OBJECT,
},
)
def retrieve(self, request, pk=None):
...
枚举字段 MUST 在 Serializer 中使用 ChoiceField 并指定 choices,drf-spectacular 会自动生成 enum 约束:
from ENUMS import PropertyType # 取自项目枚举常量
class PropertySerializer(serializers.Serializer):
property_type = serializers.ChoiceField(
choices=PropertyType.choices,
help_text="房源类型(详见 DATA_MODEL/ENUMS.md § property.property_type)"
)
分页/筛选端点额外声明 parameters:
@extend_schema(
parameters=[
OpenApiParameter("cursor", OpenApiTypes.STR, description="Keyset 游标,首页传 null"),
OpenApiParameter("limit", OpenApiTypes.INT, description="每页条数,最大 100"),
OpenApiParameter("status", OpenApiTypes.STR, description="状态筛选,多选用逗号分隔"),
]
)
11.3 Schema 文件管理(MUST)
CI pipeline MUST 包含以下步骤,防止 Schema 与代码漂移:
# 生成最新 Schema
python manage.py spectacular --color --file openapi.json
# diff 检查(有变更时 CI 提醒,但不阻断 — 由开发者 review 后提交)
git diff --exit-code openapi.json || echo "⚠️ openapi.json has changed — please review and commit"
openapi.jsonMUST 纳入版本控制(不加入.gitignore)- 合并 PR 时若
openapi.json有非预期变更,MUST 作为 Review 阻断项
11.4 契约测试规范(MUST)
使用 schemathesis 对每个模块做 Positive 路径覆盖,最低要求:
# 对本地运行的 dev server 跑契约测试
schemathesis run openapi.json \
--base-url http://localhost:8000 \
--auth-header "Authorization: Bearer *** \
--checks status_code_conformance response_schema_conformance \
--tag property # 可按模块 tag 分批跑
CI 集成示例(GitHub Actions):
- name: Contract Tests
run: |
python manage.py spectacular --file openapi.json
schemathesis run openapi.json \
--base-url http://localhost:8000 \
--checks status_code_conformance response_schema_conformance \
--exitfirst # 首个失败即停止
AI Agent 验收词(Acceptance Criteria):实现任意 API 端点后,须能通过以下验证:
GIVEN openapi.json 已重新生成
WHEN schemathesis 对该端点执行 Positive 测试
THEN status_code_conformance PASS(响应码与 Schema 声明一致)
AND response_schema_conformance PASS(响应体结构与 Serializer 一致)
AND 所有枚举字段值落在 ENUMS.md 定义的合法值集合内
11.5 枚举值契约一致性(MUST)
ENUMS.md 是枚举的单一事实来源(Source of Truth)。项目 MUST 维护一个 enums.py(或按模块拆分),与 ENUMS.md 保持同步:
# fonrey/core/enums.py — 机器可读枚举常量,与 ENUMS.md 严格对齐
from django.db import models
class PropertyType(models.TextChoices):
RESIDENTIAL = "residential", "住宅"
COMMERCIAL = "commercial", "商业"
OFFICE = "office", "办公"
INDUSTRIAL = "industrial", "工业"
LAND = "land", "土地"
OTHER = "other", "其他"
AI agent 实现时验证词:
GIVEN enums.py 中某枚举类的所有 value
WHEN 与 ENUMS.md 对应域的值列表对比
THEN 两侧完全一致(无多余值,无缺失值,大小写相同)
11.6 分阶段落地路线(参考)
| 阶段 | 目标 | 完成标志 |
|---|---|---|
| P0 接入 | 安装 drf-spectacular,生成首版 openapi.json,Swagger UI 可访问 |
/api/docs/ 正常渲染,无 import 报错 |
| 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(
adminSessionIdCookie)+ 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