草稿管理 API
本文引用的文件 - src/admin_server.v - src/admin_runtime.v - src/admin_state_runtime.v - src/admin_state_store/store.v - admin/ui/app.js
目录
简介
本文件为 vhttpd 的“草稿管理 API”完整参考文档,聚焦于 /admin/drafts 端点的 CRUD 操作(创建、查询、更新、删除),并补充校验、差异预览与发布等关键能力。文档涵盖: - 草稿 ID 生成规则 - 草稿数据结构与持久化模型 - 草稿与正式内容(运行时计划)的关系 - 典型工作流示例与协作最佳实践
项目结构
与草稿管理相关的关键代码位于以下模块: - 控制面/数据面路由与鉴权:src/admin_server.v、src/admin_runtime.v - 草稿状态与业务逻辑:src/admin_state_runtime.v - 草稿持久化存储:src/admin_state_store/store.v - 前端调用示例:admin/ui/app.js
graph TB
Client["客户端/前端"] --> AdminServer["Admin 服务器<br/>src/admin_server.v"]
Client --> DataPlane["数据面路由<br/>src/admin_runtime.v"]
AdminServer --> Shared["共享应用实例<br/>src/admin_state_runtime.v"]
DataPlane --> Shared
Shared --> Store["文件型键值存储<br/>src/admin_state_store/store.v"]
AdminUI["管理界面 JS<br/>admin/ui/app.js"] --> AdminServer
AdminUI --> DataPlane
图表来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_state_runtime.v:124-174 - src/admin_state_store/store.v:42-109 - admin/ui/app.js:794-993
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_state_runtime.v:124-174 - src/admin_state_store/store.v:42-109 - admin/ui/app.js:794-993
核心组件
- 路由层
- 控制面路由:提供鉴权后的 /admin/drafts* 接口,用于管理后台交互。
- 数据面路由:在启用 on_data_plane 时暴露相同语义的 /admin/drafts* 接口。
- 状态层
- 草稿生命周期:创建、读取、更新、删除、事件记录、元数据维护。
- 编译与校验:将草稿文本编译为运行时计划,返回诊断信息。
- 差异预览:对比当前运行计划与草稿计划,输出变更策略与允许性。
- 发布:将草稿写入目标路径,覆盖或合并到正式配置。
- 存储层
- 基于文件的命名空间键值存储,支持原子写入、事件追加与列表枚举。
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_state_runtime.v:124-174 - src/admin_state_store/store.v:42-109
架构总览
下图展示了从请求进入、鉴权、路由分发、状态处理到持久化的端到端流程。
sequenceDiagram
participant C as "客户端"
participant S as "Admin 服务器<br/>src/admin_server.v"
participant D as "数据面路由<br/>src/admin_runtime.v"
participant A as "共享应用<br/>src/admin_state_runtime.v"
participant F as "文件存储<br/>src/admin_state_store/store.v"
C->>S : "POST /admin/drafts"
S->>S : "鉴权检查"
S->>A : "保存草稿(put_draft)"
A->>F : "写入 drafts/<id>.json"
F-->>A : "Entry{key,value,updated_at}"
A-->>S : "返回 Entry"
S-->>C : "200 + Entry"
C->>D : "GET /admin/drafts/ : id"
D->>A : "获取草稿(get_draft)"
A->>F : "读取 drafts/<id>.json"
F-->>A : "Entry"
A-->>D : "返回 Entry"
D-->>C : "200 + Entry"
图表来源 - src/admin_server.v:365-385 - src/admin_runtime.v:219-240 - src/admin_state_runtime.v:129-145 - src/admin_state_store/store.v:42-72
详细组件分析
端点清单与行为
- GET /admin/drafts
- 列出所有草稿条目(命名空间 drafts)。
- 数据面需开启 on_data_plane,否则返回 404。
- POST /admin/drafts
- 创建或更新草稿。若 query.id 为空则自动生成 id。
- 返回 Entry(包含 key/value/updated_at_unix)。
- GET /admin/drafts/:id
- 按 id 获取草稿详情。
- PUT /admin/drafts/:id
- 以指定 id 更新草稿内容。
- DELETE /admin/drafts/:id
- 删除指定草稿及其元数据。
- POST /admin/drafts/:id/validate
- 校验草稿是否能编译为运行时计划,返回 ok、schema_version、count、diagnostics。
- GET /admin/drafts/:id/diff
- 计算草稿与当前运行计划的差异预览,返回 allowed、strategy 等。
- POST /admin/drafts/:id/publish
- 将草稿发布到目标路径(query.path 或 include_path),返回 ok、path、bytes 等。
注意: - 控制面路由会进行鉴权;数据面路由会在未启用 on_data_plane 时返回 404。 - 上述端点在控制面和数据面均有实现,语义一致。
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_server.v:930-985 - src/admin_runtime.v:312-357
草稿 ID 生成规则
- 若请求携带 query.id 且非空,则使用该值作为草稿 id。
- 若未提供或为空,系统自动生成 id,格式为 draft_ 前缀加微秒级时间戳。
- 该规则确保 id 唯一且可追溯。
章节来源 - src/admin_state_runtime.v:109-115
草稿数据结构与持久化
- 存储命名空间
- drafts:存放草稿正文(JSON 字符串形式)。
- draft_meta:存放草稿元数据(如 source_path、include_path 等)。
- Entry 字段
- namespace:命名空间名(如 drafts)。
- key:草稿 id。
- value:草稿正文(字符串)。
- updated_at_unix:更新时间(Unix 秒)。
- 文件布局
- 根目录由 event_log 或默认 .var/vhttpd/admin 决定。
- 每个命名空间对应一个子目录,条目以 key.json 命名。
- 并发与一致性
- put/delete/list 使用互斥锁保护。
- 写入采用临时文件+重命名的原子写策略。
章节来源 - src/admin_state_store/store.v:8-14 - src/admin_state_store/store.v:42-109 - src/admin_state_store/store.v:187-194 - src/admin_state_runtime.v:99-107
草稿与正式内容的关系
- 草稿正文是运行时计划(RuntimePlan)的文本表示。
- 校验 validate 会将草稿编译为运行时计划,返回 schema_version、计数与诊断信息。
- 差异 diff 比较草稿计划与当前运行计划,给出 allowed 与 strategy。
- 发布 publish 将草稿解析为目标路径的正式配置,并触发运行时替换流程。
章节来源 - src/admin_state_runtime.v:176-207 - src/admin_state_runtime.v:445-490
工作流序列图(创建-校验-预览-发布)
sequenceDiagram
participant U as "用户/工具"
participant API as "Admin API"
participant ST as "状态层"
participant FS as "文件系统"
U->>API : "POST /admin/drafts (body=计划文本)"
API->>ST : "保存草稿"
ST->>FS : "写入 drafts/<id>.json"
API-->>U : "返回 {draft_id, entry}"
U->>API : "POST /admin/drafts/ : id/validate"
API->>ST : "编译草稿为计划"
ST-->>API : "{ok, diagnostics, counts}"
API-->>U : "返回校验结果"
U->>API : "GET /admin/drafts/ : id/diff"
API->>ST : "计算差异预览"
ST-->>API : "{allowed, strategy}"
API-->>U : "返回差异信息"
U->>API : "POST /admin/drafts/ : id/publish?path=..."
API->>ST : "解析并发布到目标路径"
ST-->>API : "{ok, path, bytes}"
API-->>U : "返回发布结果"
图表来源 - src/admin_server.v:365-385 - src/admin_server.v:930-985 - src/admin_runtime.v:312-357 - src/admin_state_runtime.v:176-207 - src/admin_state_runtime.v:445-490
错误码与响应约定
- 成功:200,返回 JSON 对象(含 admin_endpoint、draft_id 等上下文字段)。
- 参数/校验失败:400 或 422,返回 {error}。
- 资源不存在:404。
- 权限不足:控制面返回禁止访问响应。
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357
依赖关系分析
- 路由层依赖状态层完成具体业务逻辑。
- 状态层依赖存储层进行持久化与事件记录。
- 前端通过控制面/数据面路由与后端交互。
classDiagram
class AdminServer {
"+路由 : /admin/drafts*"
"+鉴权 : admin_authorized()"
}
class DataPlane {
"+路由 : /admin/drafts*"
"+开关 : on_data_plane"
}
class AppState {
"+保存/读取/删除草稿"
"+校验/差异/发布"
"+事件记录"
}
class FileStore {
"+get/put/delete/list"
"+append_event"
"+原子写入"
}
AdminServer --> AppState : "调用"
DataPlane --> AppState : "调用"
AppState --> FileStore : "读写"
图表来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_state_runtime.v:124-174 - src/admin_state_store/store.v:42-109
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_state_runtime.v:124-174 - src/admin_state_store/store.v:42-109
性能与一致性
- 原子写入:通过临时文件+mv 保证写入一致性,避免部分写入导致损坏。
- 并发安全:put/delete/list 使用互斥锁保护,避免竞态条件。
- 事件追加:事件日志以追加模式写入,适合审计与回放。
- 建议:
- 批量操作尽量分片,避免单次过大 payload。
- 高频读取场景可在上层引入缓存(例如短 TTL 的内存缓存)。
- 对大文件发布,关注磁盘 I/O 与目标路径权限。
章节来源 - src/admin_state_store/store.v:187-194 - src/admin_state_store/store.v:111-133
故障排查指南
- 404 Not Found(数据面)
- 现象:数据面路由返回 404。
- 原因:on_data_plane 未启用。
- 处理:确认控制平面配置已开启数据面。
- 400/422 校验失败
- 现象:validate 返回 ok=false 与 error。
- 原因:草稿无法编译为运行时计划或存在诊断错误。
- 处理:根据 diagnostics 修正草稿内容。
- 403 禁止访问(控制面)
- 现象:未通过鉴权。
- 原因:缺少或错误的 admin_token。
- 处理:在请求头或查询参数中提供正确的 token。
- 404 资源不存在
- 现象:GET /admin/drafts/:id 返回 404。
- 原因:草稿 id 不存在。
- 处理:先列出草稿确认 id,或重新创建。
章节来源 - src/admin_runtime.v:136-157 - src/admin_server.v:930-985 - src/admin_server.v:365-470
结论
vhttpd 的草稿管理 API 提供了完整的草稿生命周期管理能力,结合校验、差异预览与发布,形成“编辑-验证-预览-发布”的安全闭环。其基于文件系统的持久化方案具备简单可靠、易于审计的特点,并通过原子写入与互斥锁保障一致性。建议在团队协作中遵循最小权限、明确 id 规范、充分使用 validate/diff 以降低发布风险。
附录:API 参考
通用说明
- 认证
- 控制面:需在请求头或查询参数中提供 admin_token。
- 数据面:需在控制面启用 on_data_plane。
- 请求体
- 创建/更新:正文为运行时计划文本。
- 响应体
- 成功:JSON 对象,包含 admin_endpoint、draft_id 等上下文字段。
- 失败:{error}。
端点定义
- GET /admin/drafts
- 功能:列出所有草稿条目。
- 成功响应:数组,元素为 Entry(namespace/key/value/updated_at_unix)。
-
错误:数据面未启用时返回 404。
-
POST /admin/drafts
- 功能:创建或更新草稿。
- 查询参数:id(可选,为空则自动生成)。
- 请求体:运行时计划文本。
- 成功响应:Entry。
-
错误:400(参数或写入错误)。
-
GET /admin/drafts/:id
- 功能:获取指定草稿详情。
- 成功响应:Entry。
-
错误:404(不存在)、400(读取错误)。
-
PUT /admin/drafts/:id
- 功能:更新指定草稿内容。
- 请求体:运行时计划文本。
- 成功响应:Entry。
-
错误:400(写入错误)。
-
DELETE /admin/drafts/:id
- 功能:删除指定草稿及元数据。
- 成功响应:{ok:true, draft_id}。
-
错误:400(删除错误)。
-
POST /admin/drafts/:id/validate
- 功能:校验草稿能否编译为运行时计划。
- 成功响应:{ok, schema_version, counts, diagnostics}。
-
错误:422(编译失败)。
-
GET /admin/drafts/:id/diff
- 功能:预览草稿与当前运行计划的差异。
- 成功响应:{allowed, strategy}。
-
错误:422(预览失败)。
-
POST /admin/drafts/:id/publish
- 功能:将草稿发布到目标路径。
- 查询参数:path 或 include_path(二选一)。
- 成功响应:{ok, path, bytes}。
- 错误:422(发布失败)。
章节来源 - src/admin_server.v:365-470 - src/admin_runtime.v:136-357 - src/admin_server.v:930-985 - src/admin_runtime.v:312-357
使用示例(来自前端)
- 列举草稿:GET /admin/drafts?id=
- 获取草稿:GET /admin/drafts/
- 更新草稿:PUT /admin/drafts/
- 校验草稿:POST /admin/drafts/
/validate - 差异预览:GET /admin/drafts/
/diff - 发布草稿:POST /admin/drafts/
/publish?path= - 删除草稿:DELETE /admin/drafts/
章节来源 - admin/ui/app.js:794-993
协作开发最佳实践
- 统一 id 命名:建议使用业务前缀+时间戳,便于追踪。
- 强制校验:在提交前调用 validate,确保语法与结构正确。
- 差异审查:发布前调用 diff,评估影响范围与策略。
- 灰度发布:优先选择 include_path 指向独立片段,降低主配置风险。
- 审计回溯:利用事件日志与 updated_at_unix 进行变更审计。
- 权限隔离:严格控制 admin_token,仅授权必要人员。
[本节为概念性指导,不直接分析具体文件]