Scert/.claude/docs/ai/project-members/api-handoff.md

132 lines
7.4 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.

# Scert 项目成员接口交接
> 本文件保留成员接口批次契约。新增可见项目入口和成员页面的当前交接见同目录 `api-handoff-v2.md`;下方“不提供项目列表或页面”描述仅对应原批次。
## 业务范围与交付边界
项目成员关系用于管理同一项目内的六类角色,与用户分组独立。普通用户可以担任项目管理员,普通全局管理员不因登录角色或同组身份自动取得项目权限。接口允许当前有效超级管理员或目标项目的项目管理员管理成员。
本批提供成员列表、授予与撤销接口,不提供项目列表/创建入口或成员页面,也不代表域名、证书、服务器、DNS 等全部业务资源已经接入项目授权。源码专项、实库与构建证据分别记录于《构建版本与数据库支持矩阵》第二十三节,不据此声明生产验收通过。
## 通用约定
- 前缀:`/api/v1/projects/:projectID/members`,路径必须使用实际项目标识。
- 使用正式登录接口建立的会话 Cookie;浏览器请求同源发送。服务器验证提供的 `Origin`/`Referer`,来源拒绝返回 403。
- 不传 `actor_id`、全局角色或项目权限;操作者仅取本次服务端验证的会话,仓储执行时重新核对账户状态和成员关系。
- 项目、用户及游标输入仅允许 1~128 位 ASCII 字母、数字、连字符、下划线;空游标例外。已有项目实体本身仍遵守其更严格的命名规则,此接口不创建项目。
- 响应头 `X-Scert-Request-ID` 可用于关联诊断。错误响应为 `{"error":"中文提示","request_id":"请求编号"}`,不能仅按文本匹配决定权限。
- 成功授权/撤销和成功审计在同一事务提交;失败时不得乐观更新界面。成功变更后应重新获取第一页,清空旧游标栈。
## 接口
### GET:读取成员列表
```http
GET /api/v1/projects/default/members?page_size=10&after=user-001
```
| 查询参数 | 类型 | 约束 |
|---|---|---|
| `page_size` | 整数 | 默认 10;只接受 5、10、15、30 |
| `after` | 字符串 | 默认空,从第一页读取;下一页使用上一响应的 `next_cursor` |
重复参数、未知参数、非法转义、无效页大小或游标返回 400。没有页码或总人数参数。
成功返回 200:
```json
{
"items": [
{
"user_id": "user-002",
"username": "测试成员",
"status": "active",
"roles": ["dns_admin", "read_only"]
}
],
"page_size": 10,
"has_more": false,
"next_cursor": ""
}
```
- 按用户 ID 升序的游标分页,不按角色关系计数,同一账户的角色合并到一个成员项中。
- `items` 始终为数组;合法空项目和末尾空页为 `[]`,不是未授权。`has_more=false` 时 `next_cursor` 为空。
- 列表包含仍有成员关系的停用账户,便于撤销其角色;停用账户自身无法操作接口。
- 不返回邮箱、密码摘要、用户分组或全局角色。角色顺序由接口稳定排序,界面应按角色值映射中文名称。
- 超级管理员不隐式出现在成员列表中;只有显式成员关系才列出。空默认项目不会自动增加成员。
- 游标不是快照。翻页期间发生授权变更时,应允许用户刷新并回到第一页,不承诺跨请求一致性。
### POST:授予角色
```http
POST /api/v1/projects/default/members
Content-Type: application/json
```
```json
{
"user_id": "user-002",
"role": "dns_admin"
}
```
- 目标必须为真实有效账户,不能凭用户名或邮箱替代 `user_id`。
- 正文只接受 `user_id`、`role`,拒绝未知字段、多段 JSON 和超过 64 KiB 的正文;不接受查询参数。
- 成功为 201:`{"message":"项目角色已授予"}`。
- 已存在相同项目/账户/角色关系返回 409,不生成重复关系;其他角色可另行授予。
- 同一账户可在多个项目持有多个角色,但该规则不改变普通用户只能属于一个用户分组的限制。
### DELETE:撤销角色
```http
DELETE /api/v1/projects/default/members/user-002/roles/dns_admin
```
- 不接受请求体或查询参数,空白请求体也拒绝。不能使用 DELETE 正文冒充操作者。
- 成功为 204,无响应体;客户端不要尝试解析成功响应 JSON。
- 不存在的目标关系与权限不足均返回 403,不作为幂等成功。
- 允许项目管理员撤销自己的项目管理员角色。成功后应退出成员管理状态或处理重新读取产生的 403,不能重试为自己恢复权限;有效超级管理员仍可恢复授权。
## 角色枚举与显示名称
| 值 | 中文名称 |
|---|---|
| `project_admin` | 项目管理员 |
| `certificate_admin` | 证书管理员 |
| `deployment_admin` | 部署管理员 |
| `dns_admin` | DNS 管理员 |
| `audit_viewer` | 审计查看者 |
| `read_only` | 只读用户 |
`super_admin` 不是可授予的项目角色。角色能力矩阵见《项目资源关联模型》9.1 节;不能因为名称相近就授予未接入资源的权限。普通账户不能读取完整私钥的上限不因项目管理员角色而解除。
## 错误与审计边界
| HTTP 状态 | 含义与界面处理 |
|---|---|
| 400 | 输入无效;显示中文提示,不重试相同请求 |
| 401 | 未登录、会话失效或账户已停用;重新认证 |
| 403 | 来源拒绝、无项目权限、项目不存在、目标账户/关系不满足条件;不区分这些情况枚举他人资源 |
| 409 | 项目成员关系已存在,或系统尚未初始化;以当前流程和错误响应区分,不当作成功 |
| 500 | 操作或事务审计失败;不展示底层数据库错误,不乐观认为授权已生效 |
| 503 | 成员服务未接入或不可用;禁止将空白页面解释为没有成员 |
会话验证后、仓储执行前发生撤权或停用仍会拒绝。失败审计保留本次已验证身份;未认证请求不归到客户端声明的账户。失败审计独立于回滚事务,记录固定动作、HTTP 状态、目标及请求编号,不保存请求正文或任意底层错误。请求取消后审计仍有独立的三秒上限;审计存储自身失败时只写稳定服务器诊断日志,原失败响应保持不变,不能承诺事件已持久化。
## 前端集成与验收场景
1. 按账户分页并显示中文角色标签;用户可选择 5、10、15、30 条,切换页大小回到第一页。
2. 保存各页起始游标用于上一页;不将 `next_cursor` 当作页码,不伪造总人数。
3. 授权提交后等待 201,再刷新列表;重复授权 409 显示冲突并刷新,不生成本地重复项。
4. 撤销前确认目标与角色,自我撤权单独提示;204 后不解析 JSON,失去权限时停止继续操作。
5. 分别验证普通全局管理员、只读项目成员、其他项目管理员均无法管理本项目成员。
6. 测试空列表、末页、停用成员、多角色账户和翻页期间关系变更;不显示邮箱或其他未返回字段。
7. 401/403/500/503 分别显示正确状态,不能统一伪装为空列表;诊断信息展示请求编号而非数据库原文。
## 待完成
- [ ] 项目列表/创建及入口选择、授权目标账户选择、成员页面与浏览器验收。
- [ ] 全部业务资源与排队任务执行/取消/重试的权限重判。
- [ ] PostgreSQL 当前实库、Linux 原生及完整生产部署验收;已通过的局部测试不替代这些门禁。