拆分域名表单逻辑,保留部分成功状态,防止重复创建和读取失败误清空关联;补充组合式状态、页面接线及模板回归源码,同步任务与审查文档。本批遵循先开发后统一测试,未执行测试、构建或部署。
8.2 KiB
项目生命周期、资源读取及多对多绑定接口交接
日期:2026-09-28。本文对应已编写但尚未统一测试的源码;不表示接口、浏览器或生产部署验收通过。前两版保留历史范围,不修改作为 Smail 参考的《后端接口说明文档》。
业务范围与认证
域名与服务器是同项目内的多对多关系,一个域名可部署多台服务器,一台服务器也能承载多个域名。取消关联只改变当前域名,不删除服务器或远端文件、不影响其他域名。保存关联不执行 SSH、证书签发或推送。
使用原有会话 Cookie,操作者只能取服务端身份。全局普通管理员和用户分组不隐式授予项目权限;账户停用、成员撤权必须实时拒绝。成功和失败响应均使用 Cache-Control: private, no-store、Vary: Cookie。跨来源写入由现有保护拒绝。
项目生命周期
| 接口 | 请求 | 成功响应 |
|---|---|---|
POST /api/v1/projects |
{"name":"生产证书","description":"生产环境"} |
201,项目对象 |
PUT /api/v1/projects/:projectID |
{"name":"生产证书","description":"更新说明"} |
200,{"message":"项目资料已更新"} |
DELETE /api/v1/projects/:projectID |
{"confirmation":"目标项目标识"} |
204,无正文 |
- 创建只允许活跃超级管理员,编辑/删除依实际项目管理权限;前端能力字段仅用于显示,不作为授权凭证。
- 标识由服务端创建,格式
^[a-z0-9][a-z0-9_-]{0,35}$;请求不得伪造标识、操作者或其他未知字段,不接受查询参数。 - 名称去除首尾空白后为 1~128 个 Unicode 字符,不含控制字符;说明最多 4096 个字符,允许换行、回车和制表符。
- 默认项目不可删除,资源或成员尚有关联也不能删除。删除确认必须与目标 ID 一致,不代替用户自动清理资源。
- 参数错误 400、未认证 401、无权或未知项目 403、默认项/引用冲突 409、依赖未就绪 503、内部错误 500。错误为
{"error":"中文提示"}。 - 成功变更和审计同事务。前端生命周期与成员操作共用忙状态,禁止相互冲突的重复提交;切换项目或失去权限后丢弃旧响应。
资源读取入口
| 接口 | 权限 | 返回 |
|---|---|---|
GET /api/v1/domains |
certificate.read |
当前可见域名 items |
GET /api/v1/servers |
server.read |
当前可见服务器 items |
GET /api/v1/servers/:id |
server.read |
服务器对象 |
GET /api/v1/domains/:id/servers |
双方读取权限,同项目 | 关联服务器 items |
GET /api/v1/certificates |
certificate.read |
当前可见证书元数据 items |
GET /api/v1/certificates/:id |
域名的 certificate.read |
证书元数据 |
GET /api/v1/domains/:id/certificate/versions |
certificate.read |
版本 items |
沿用原有响应字段;本批资源列表没有新增游标参数,不与项目目录分页混淆。列表在 SQL 内过滤,多角色不重复返回;空列表为 [] 而不是 null。未知与越权详情均为 403,缺少授权依赖为 503,不回退全量基础仓储。证书文件读取前先检查实际域名权限。
多对多关联写入
PUT /api/v1/domains/:id/servers
请求必须明确包含数组:
{"server_ids":["server-one","server-two"]}
清空当前域名:
{"server_ids":[]}
- 成功 200,
{"items":[服务器对象]};清空返回{"items":[]}。按服务器名称、ID 排序,不能依赖请求顺序。 - 有效超级管理员可操作;其他活跃账户需同时具备当前项目的
certificate.read、server.read、deployment.execute。当前角色矩阵中项目管理员和部署管理员符合,多个角色能力可合并。 - 所有服务器必须与实际域名同项目,包括超级管理员;不接受客户端
project_id、actor_id或任何查询参数。 - ID 为 1~128 位 ASCII 字母、数字、连字符或下划线。最多输入 100 个服务器 ID,上限按去重前计算;重复 ID 去重后仅保存一条关联。
- 缺字段、
null、错误类型、未知字段、非法 ID、超限为 400;无权、跨项目或未知资源统一 403;已知共享路径冲突为 409;缺依赖为 503;内部失败为 500。401 表示未认证。 - 成功审计
domain.servers.replace与关联同事务,失败不清空旧绑定。失败审计根据实际域名归属写入,不保存请求体、密钥或原始数据库错误。
路径隔离与错误处理
默认目标为 /ssl/{域名}/{证书类型}/ 下的 cert.pem、fullchain.pem、key.pem,也接受英文占位符 {domain}、{certificate_type}。
绑定保存、服务器配置修改及部署预检会比较实际展开并规范化后的各文件路径。多个域名共用固定路径、占位符被 .. 抵消、不同文件类型相互占用或文件目录重叠时拒绝;单个域名的固定路径仍可使用。409 只返回中文修正提示,不暴露其他域名或私钥路径。
前端保留多选复选框,不因服务器已被其他域名使用而禁用或复制服务器。说明放入 HelpTip。409 时保留用户选择,引导修改服务器模板后重试;无权限或未知资源不显示区别提示。不要乐观清空列表,500/网络失败需刷新确认真实状态。
域名资料保存与绑定保存仍是两个请求,不应显示为跨请求原子提交。前端已拆分 DomainForm.js:收到完整资料成功响应后立即保留域名标识和列表项,关联失败时保留用户选择;资料未变更只重试关联,资料变化则更新既有域名,不重复创建。创建网络异常、服务端错误或缺少标识/项目字段时禁止在当前表单中盲目重发,提示关闭并刷新确认;这不等于服务端幂等键或跨刷新恢复。
编辑表单先重新读取真实关联,读取失败或缺少 items 数组时禁用保存并提供重读入口,不能把错误解释为空数组。仅展示域名同项目服务器,不按其他域名是否占用过滤;未知项目归属拒绝保存,新建沿用后端当前默认项目。保存期间禁用编辑和重复提交,关闭/切换以及卸载后的旧响应不得污染新表单。资料与关联均成功但列表刷新失败时提示刷新失败,不误报保存失败。全部新增逻辑仅开发,等待统一测试。
当前未覆盖的边界
- 本批路径预检只针对同一服务器记录的当前绑定,不识别多个记录指向同一主机、远端别名或解除绑定后的遗留文件;配置并发变化与在途任务的完整路径占用协调仍待部署整改。
- 其他资源 CRUD、签发/导入/部署、DNS/SSH、任务读取/执行/取消/重试和审计读取的项目权限仍待补齐。
- 本批只编写回归源码,尚未执行测试、构建或浏览器验收,旧批次通过结果不可复用为本批证据。
待统一执行的关键场景
- A、B 两个域名均关联 S1、S2;分别读取均得到两台服务器,重复输入不产生重复关联。
- 修改或清空 A,B 的两条关联和服务器本身保持不变;只有显式空数组才能清空。
- 跨项目、越权、停用/撤权、未知资源、缺依赖或审计故障均不改变旧关联。
- 两个域名在同一服务器解析为不同路径;固定私钥/证书/链路径、规范化和跨文件冲突拒绝。
- 修改服务器不能引入共享冲突;历史危险配置必须在部署前被拒绝,不能调用远端写入。
- Vue 模板同时渲染两个已选复选框及悬浮提示;SSR 回归不代替真实浏览器、SSH 或多服务器生产推送验收。
- 创建成功、关联冲突或网络异常时保留域名与选择,重试不再次创建;资料变更更新已有标识,取消不会隐藏已创建列表项。
- 关联读取失败不得提交清空;重新读取成功后恢复选择。跨项目/缺归属服务器不显示为候选,切换表单的旧读取响应不能覆盖新表单。
- 重复点击、保存期间切换、卸载和资料响应不完整时不发起多余写请求;失败状态、按钮禁用及中文提示一致。上述回归源码未执行。