EternalAI/docs/plans/2026-06-21-001-feat-admin-r...

20 KiB
Raw Blame History

Plan: 管理员审核 + Hermes 同步工作流

Status: active Created: 2026-06-21 Origin: docs/brainstorms/2026-06-20-hermes-cross-machine-deploy-requirements.md (演进) Plan depth: Standard


Summary

实现「管理员审核 + 管理员发起 Hermes 同步」的新工作流。角色创建后进入待审核状态管理员后台审核通过后填写同步参数profile 名、主 model key/服务商、多媒体 model key/服务商、定时任务开关EternalAI 用一次性 sync_token 向 Hermes 发起同步请求Hermes 回调拉取文件并创建 profile返回绑定二维码。测试范围限定为鉴权和文件拉取无真实 Hermes用 mock 端点验证)。


Problem Frame

当前流程:创作者创建角色后直接上架,用户自行用 API Key + curl 拉取配置到 Hermes。存在以下问题

  1. 缺少内容审核环节,任何人设都可直接发布
  2. 同步由用户手动操作,无法集中管控
  3. Hermes 配置参数model key、服务商等分散在用户侧不一致

新流程:管理员集中审核 + 管理员发起同步 + Hermes 回调拉取 + 二维码绑定。


Requirements

功能需求

  • R1: 角色创建后状态为 pending_review,需管理员审核才能上架
  • R2: 独立 Admin 表,管理员有独立登录入口
  • R3: 管理员后台可查看待审核列表、角色详情,通过/驳回
  • R4: 审核通过后管理员填写同步参数并发起同步
  • R5: EternalAI 用一次性 sync_tokenHMAC SHA-256 对称密钥)向 Hermes POST 同步请求
  • R6: Hermes 用 sync_token 回调 EternalAI 拉取 SOUL.md 和 config.yaml
  • R7: Hermes 返回二维码 URLEternalAI 存储并展示给管理员和创作者
  • R8: Hermes webhook URL 全局配置,同步时可覆盖
  • R9: 测试范围鉴权admin 登录、sync_token 验证)+ 文件拉取mock Hermes 回调)

非功能需求

  • sync_token 5 分钟过期,一次性消费
  • Hermes 回调拉取端点同时支持 sync_token 和现有 API Key 认证
  • 现有用户侧 API Key 拉取流程保留不变

Key Technical Decisions

KTD1: 独立 Admin 表

Admin 与 User 分离,独立登录接口,不混用 JWT。

理由: 用户选择。管理员权限边界清晰,避免 User 表 isAdmin 字段的权限提升风险。

KTD2: sync_token 用 HMAC SHA-256 对称密钥

EternalAI 和 Hermes 预共享 SYNC_SECRETsync_token 是 JWTHS256payload 含 roleIdadminIdiatexp5 分钟)。

理由: 用户选择。对称密钥实现简单,双方预共享即可。

KTD3: Hermes webhook URL 全局配置 + 可覆盖

系统设置表存储 HERMES_WEBHOOK_URL,管理员发起同步时可在表单中覆盖。

理由: 用户选择。大多数情况用全局配置,特殊场景可覆盖。

KTD4: 二维码由 Hermes 生成返回

Hermes 创建 profile 后生成二维码 URL 返回给 EternalAIEternalAI 存储到 Role 记录并展示。

理由: 用户选择。二维码内容(微信绑定链接)由 Hermes 侧定义。

KTD5: 角色审核状态机

pending_review → approved → syncing → synced
pending_review → rejected
syncing → failed

Role 模型新增 reviewStatus 字段(默认 pending_review),新增 qrCodeUrlsyncedAt 字段。

KTD6: Mock Hermes 测试端点

在 EternalAI 内部创建 /api/mock-hermes/* 端点模拟 Hermes 行为:接收同步请求、用 sync_token 回调拉取文件、返回 mock 二维码。仅测试环境启用。


High-Level Technical Design

同步流程时序图

sequenceDiagram
    participant Admin as 管理员
    participant EAI as EternalAI
    participant Hermes as Hermes (Mock)

    Admin->>EAI: POST /api/admin/sync/:roleId
    Note over EAI: 生成 sync_token (JWT, 5min)
    EAI->>Hermes: POST {webhook_url} /api/sync
    Note over EAI: Body: { profileName, modelKey,<br/>provider, multimediaModelKey,<br/>multimediaProvider, enableSchedule,<br/>sync_token, file_pull_base_url }
    Hermes->>Hermes: 验证 sync_token 签名
    Hermes->>EAI: GET /api/hermes/roles/:id/SOUL.md
    Note over Hermes: Header: X-Sync-Token
    EAI->>EAI: 验证 sync_token (签名+过期+未消费)
    EAI-->>Hermes: SOUL.md content
    Hermes->>EAI: GET /api/hermes/roles/:id/config.yaml
    Note over Hermes: Header: X-Sync-Token
    EAI-->>Hermes: config.yaml content
    Note over Hermes: 创建 profile, 生成二维码
    Hermes-->>EAI: 200 { qrCodeUrl, profileId }
    Note over EAI: 存储 qrCodeUrl, 更新 reviewStatus=synced
    EAI-->>Admin: 200 { qrCodeUrl, reviewStatus }

角色审核状态机

stateDiagram-v2
    [*] --> pending_review : 创建角色
    pending_review --> approved : 管理员通过
    pending_review --> rejected : 管理员驳回
    approved --> syncing : 管理员发起同步
    syncing --> synced : Hermes 返回成功
    syncing --> failed : 同步失败/超时
    failed --> syncing : 重新同步

Implementation Units

U1. Admin 数据模型与认证

Goal: 创建独立 Admin 表,实现管理员注册/登录/中间件。

Requirements: R2

Dependencies:

Files:

  • prisma/schema.prisma — 新增 Admin 模型
  • src/lib/auth.js — 新增 adminSignTokenadminVerifyTokenadminAuthMiddleware
  • src/routes/admin-auth.js — 新建,管理员登录路由
  • server.js — 注册 /api/admin-auth 路由
  • e2e/admin-auth.spec.js — 新建,管理员认证测试

Approach:

  • Admin 模型:idaccount(唯一)、passwordbcryptcreatedAt
  • Admin JWT 与用户 JWT 使用不同 secretADMIN_JWT_SECRET),防止跨角色伪造
  • adminAuthMiddleware 验证 Admin JWT设置 req.adminId
  • 管理员账号通过 prisma db seed 或 CLI 脚本创建,不开放注册 API

Test scenarios:

  • 管理员登录成功,返回 admin JWT
  • 管理员登录密码错误,返回 401
  • 无 token 访问管理员接口,返回 401
  • 用户 JWT 访问管理员接口,返回 403secret 不同,验证失败)
  • 管理员 JWT 访问用户接口,返回 401用户中间件不识别 admin token

Verification: 管理员可登录admin JWT 可访问管理员接口,用户 JWT 不可访问管理员接口。


U2. 角色审核状态机与审核 API

Goal: Role 模型新增审核状态字段,实现审核 API。

Requirements: R1, R3

Dependencies: U1

Files:

  • prisma/schema.prisma — Role 模型新增 reviewStatusqrCodeUrlsyncedAtreviewNote 字段
  • src/routes/admin.js — 新建,管理员审核路由
  • server.js — 注册 /api/admin 路由
  • e2e/admin-review.spec.js — 新建,审核流程测试

Approach:

  • reviewStatus 枚举值:pending_review(默认)、approvedrejectedsyncingsyncedfailed
  • 现有 POST /api/roles 创建角色时自动设置 reviewStatus = 'pending_review'
  • GET /api/admin/reviews — 待审核列表(分页,按 createdAt desc
  • GET /api/admin/reviews/:roleId — 角色详情(含所有字段)
  • POST /api/admin/reviews/:roleId/approve — 通过审核,状态 → approved
  • POST /api/admin/reviews/:roleId/reject — 驳回,状态 → rejectedbody 含 reviewNote
  • 角色库 GET /api/roles 只返回 reviewStatus = 'synced' 的角色(已同步完成才上架)

Test scenarios:

  • 创建角色后 reviewStatus 为 pending_review
  • 管理员获取待审核列表,包含 pending_review 角色
  • 管理员通过审核,状态变为 approved
  • 管理员驳回审核,状态变为 rejectedreviewNote 有值
  • 非管理员调用审核接口,返回 401
  • 角色库不显示 pending_review / approved / rejected 状态的角色

Verification: 审核状态流转正确,非管理员无法操作。


U3. sync_token 机制与系统配置

Goal: 实现 sync_token 生成/验证,系统配置存储 Hermes webhook URL。

Requirements: R5, R8

Dependencies: U1

Files:

  • prisma/schema.prisma — 新增 SystemConfig 模型key-value 存储)
  • src/lib/sync-token.js — 新建sync_token 生成与验证
  • src/routes/admin-config.js — 新建,系统配置管理路由
  • server.js — 注册 /api/admin/config 路由
  • e2e/sync-token.spec.js — 新建sync_token 测试

Approach:

  • SystemConfig 模型:key(唯一)、valueupdatedAt
  • 预置配置项:HERMES_WEBHOOK_URLSYNC_SECRET
  • sync-token.js:
    • generateSyncToken(roleId, adminId) — 生成 JWTHS256payload { roleId, adminId, iat, exp }5 分钟过期
    • verifySyncToken(token) — 验证签名 + 过期,返回 payload 或 null
    • 使用 SYNC_SECRET 从 SystemConfig 读取(首次启动自动生成)
  • PUT /api/admin/config/:key — 更新配置项(仅管理员)
  • GET /api/admin/config — 获取所有配置(仅管理员,敏感值脱敏)

Test scenarios:

  • 生成 sync_token验证签名通过payload 正确
  • 过期 token>5min验证失败
  • 篡改 payload 后验证失败(签名不匹配)
  • 管理员更新 HERMES_WEBHOOK_URL再次读取值正确
  • 非管理员访问配置接口,返回 401

Verification: sync_token 生成/验证正确,系统配置可读写。


U4. 同步发起 API

Goal: 管理员发起同步EternalAI 向 Hermes POST 请求。

Requirements: R4, R5, R6

Dependencies: U2, U3

Files:

  • src/routes/admin-sync.js — 新建,同步发起路由
  • src/lib/hermes-client.js — 新建Hermes HTTP 客户端
  • server.js — 注册 /api/admin/sync 路由
  • e2e/admin-sync.spec.js — 新建,同步发起测试

Approach:

  • POST /api/admin/sync/:roleId — 发起同步
    • Body: { profileName, modelKey, provider, multimediaModelKey, multimediaProvider, enableSchedule, webhookUrl? }
    • webhookUrl 可选,未提供则用 SystemConfig 中的 HERMES_WEBHOOK_URL
    • 前置检查:reviewStatus 必须为 approvedfailed
    • 生成 sync_token更新 reviewStatus = 'syncing'
    • 调用 hermes-client.jspostSync(webhookUrl, payload) 向 Hermes POST
    • Hermes 返回 { qrCodeUrl, profileId } → 存储 qrCodeUrl,更新 reviewStatus = 'synced',记录 syncedAt
    • Hermes 返回错误 → 更新 reviewStatus = 'failed'
    • 请求超时30s→ 更新 reviewStatus = 'failed'
  • hermes-client.js:
    • postSync(webhookUrl, payload) — 用 fetch POST 到 Hermespayload 含 sync_token 和 file_pull_base_url
    • file_pull_base_url = EternalAI 自身的基础 URL从 SystemConfig 读取 ETERNALAI_BASE_URL

Test scenarios:

  • 管理员对 approved 角色发起同步reviewStatus 变为 syncing
  • Mock Hermes 返回成功reviewStatus 变为 syncedqrCodeUrl 有值
  • Mock Hermes 返回错误reviewStatus 变为 failed
  • 对 pending_review 角色发起同步,返回 400
  • 对 syncing 角色发起同步,返回 409重复同步
  • 非管理员发起同步,返回 401

Verification: 同步发起后状态流转正确,成功时存储二维码 URL。


U5. Hermes 回调拉取端点改造

Goal: 改造现有 /api/hermes/ 端点,支持 sync_token 认证。

Requirements: R6

Dependencies: U3

Files:

  • src/routes/hermes.js — 改造,新增 sync_token 认证路径
  • src/lib/auth.js — 新增 syncTokenMiddleware
  • e2e/hermes-callback.spec.js — 新建,回调拉取测试

Approach:

  • 新增 syncTokenMiddleware:
    • 读取 X-Sync-Token header
    • 验证 sync_token 签名 + 过期
    • 从 payload 提取 roleId,与 URL 中的 :id 比对,不一致返回 403
    • 验证通过后设置 req.userId(通过 Role.creatorId 反查)和 req.syncTokenPayload
  • 改造 apiKeyMiddleware 逻辑:
    • 优先检查 X-Sync-Token header → 走 sync_token 路径
    • 否则检查 Authorization: Bearer eak_ → 走 API Key 路径
    • 否则检查 Authorization: Bearer <jwt> → 走 JWT 路径
  • sync_token 消费后标记为已使用(内存 Set5 分钟后自动清理;或用 token jti + 短期缓存)
  • SOUL.md 和 config.yaml 端点保持 text/plain 响应不变

Test scenarios:

  • 用有效 sync_token 拉取 SOUL.md返回 200 + 文件内容
  • 用有效 sync_token 拉取 config.yaml返回 200 + 文件内容
  • sync_token 中的 roleId 与 URL :id 不匹配,返回 403
  • 过期 sync_token返回 401
  • 已消费的 sync_token 再次使用,返回 401
  • 无 token 访问,返回 401
  • 用 API Keyeak_访问仍正常工作向后兼容

Verification: sync_token 可拉取文件,向后兼容 API Key 认证。


U6. 二维码存储与状态展示

Goal: 存储 Hermes 返回的二维码 URL展示给管理员和创作者。

Requirements: R7

Dependencies: U4

Files:

  • src/routes/roles.js — 改造,GET /api/roles/my/roles 返回 reviewStatus 和 qrCodeUrl
  • src/routes/admin.js — 改造,管理员可查看所有角色的同步状态
  • app.js — 改造,创作者角色卡片显示审核状态和二维码
  • e2e/qr-display.spec.js — 新建,二维码展示测试

Approach:

  • GET /api/roles/my/roles 返回字段新增 reviewStatusqrCodeUrlsyncedAt
  • 创作者角色卡片根据 reviewStatus 显示状态标签:
    • pending_review → "待审核"(灰色)
    • approved → "已通过,等待同步"(蓝色)
    • syncing → "同步中"(黄色)
    • synced → "已同步"(绿色)+ 显示二维码
    • failed → "同步失败"(红色)
    • rejected → "已驳回"(红色)
  • synced 状态的角色卡片显示二维码图片(qrCodeUrl)和"转发二维码"按钮
  • 管理员后台有"同步状态"页面,显示所有角色的审核+同步状态

Test scenarios:

  • 创作者查看自己的角色列表pending_review 角色显示"待审核"标签
  • 同步成功后,创作者角色卡片显示二维码图片
  • 驳回角色显示"已驳回"标签
  • 管理员查看同步状态列表,包含所有角色的 reviewStatus

Verification: 创作者和管理员都能看到正确的审核状态和二维码。


U7. 管理员后台 UI

Goal: 管理员登录页 + 审核列表 + 角色详情审核页 + 同步表单。

Requirements: R2, R3, R4

Dependencies: U1, U2, U4

Files:

  • index.html — 新增管理员视图admin-login、admin-reviews、admin-sync
  • app.js — 新增管理员路由、审核交互、同步表单
  • styles.css — 管理员后台样式
  • e2e/admin-ui.spec.js — 新建,管理员 UI 测试

Approach:

  • 管理员入口:首页底部隐藏链接 /admin,或直接访问 #admin-login
  • 管理员登录页:账号 + 密码,登录后跳转 #admin-reviews
  • 审核列表页:表格显示角色名、创作者、创建时间、状态;点击进入详情
  • 角色详情审核页:显示所有角色字段,"通过"和"驳回"按钮
  • 同步表单(审核通过后显示):
    • profile 名字text默认角色名
    • 主 model 服务商selectopenrouter / together / local
    • 主 model keypassword input
    • 多媒体 model 服务商select
    • 多媒体 model keypassword input
    • 是否开启定时任务checkbox
    • Hermes webhook URLtext默认全局配置值可覆盖
    • "发起同步"按钮
  • 同步成功后显示二维码图片和"复制链接"按钮

Test scenarios:

  • 管理员登录后跳转审核列表
  • 审核列表显示待审核角色
  • 点击角色进入详情,显示完整信息
  • 通过审核后显示同步表单
  • 填写同步参数并提交,显示同步中状态
  • 同步成功后显示二维码

Verification: 管理员可完成登录→审核→同步的完整 UI 流程。


U8. Mock Hermes 测试端点

Goal: 在 EternalAI 内部创建 mock Hermes 端点,用于测试同步流程。

Requirements: R9

Dependencies: U3, U5

Files:

  • src/routes/mock-hermes.js — 新建mock Hermes 端点
  • server.js — 注册 /api/mock-hermes 路由(仅非 production 环境)
  • e2e/mock-hermes.spec.js — 新建mock Hermes 集成测试

Approach:

  • POST /api/mock-hermes/sync — 模拟 Hermes 接收同步请求
    • 验证 sync_token 签名(用同一 SYNC_SECRET
    • 用 sync_token 回调 EternalAI 拉取 SOUL.md 和 config.yaml
    • 返回 mock 二维码 URLhttps://mock.hermes.local/qr/<roleId>
    • 返回 mock profileIdmock-profile-<roleId>
  • Mock 端点用 fetch 回调 EternalAI 自身的 /api/hermes/roles/:id/SOUL.md/api/hermes/roles/:id/config.yaml
  • 回调时携带 X-Sync-Token header
  • 仅在 NODE_ENV !== 'production' 时注册路由

Test scenarios:

  • POST /api/mock-hermes/sync 收到请求后,回调拉取 SOUL.md 成功
  • POST /api/mock-hermes/sync 回调拉取 config.yaml 成功
  • 返回 mock 二维码 URL 和 profileId
  • 无效 sync_tokenmock Hermes 返回 401
  • production 环境下 /api/mock-hermes 路由不存在404

Verification: Mock Hermes 完整模拟同步流程,可用于 E2E 测试。


U9. E2E 集成测试:完整审核+同步流程

Goal: 端到端测试从创建角色到同步成功的完整流程。

Requirements: R1-R9

Dependencies: U1-U8

Files:

  • e2e/full-sync-flow.spec.js — 新建,完整流程测试

Approach: 测试完整流程:

  1. 创作者注册 → 登录 → 创建角色(状态 pending_review
  2. 管理员登录 → 查看待审核列表 → 查看详情 → 通过审核
  3. 管理员填写同步参数 → 发起同步
  4. Mock Hermes 接收请求 → 回调拉取文件 → 返回二维码
  5. 创作者查看角色列表 → 看到已同步状态 + 二维码
  6. 管理员查看同步状态 → 看到已同步

Test scenarios:

  • 完整流程:创建 → 审核 → 同步 → 二维码展示
  • 驳回流程:创建 → 审核 → 驳回 → 创作者看到"已驳回"
  • 同步失败流程:创建 → 审核 → 同步mock 返回错误)→ 状态 failed → 重新同步
  • 向后兼容:现有 API Key 拉取流程仍正常工作

Verification: E2E 测试覆盖所有核心路径,全部通过。


Scope Boundaries

In Scope

  • Admin 表与认证
  • 角色审核状态机
  • sync_token 生成/验证/消费
  • 同步发起 API
  • Hermes 回调拉取端点改造sync_token 认证)
  • 二维码存储与展示
  • 管理员后台 UI
  • Mock Hermes 测试端点
  • E2E 测试(鉴权 + 文件拉取)

Out of Scope

  • 真实 Hermes 服务器对接(用 mock 代替)
  • 微信公众号/小程序绑定实现(二维码内容由 Hermes 侧定义)
  • 管理员注册 UI通过 seed 脚本创建)
  • 速率限制(已有 P2 待修复,不在本次范围)
  • 现有 API Key 拉取流程的改造(保持向后兼容)

Deferred to Follow-Up Work

  • 真实 Hermes 服务器对接(需 Hermes 侧实现 /api/sync 端点)
  • 微信绑定流程实现
  • 同步重试机制(指数退避)
  • 同步日志审计
  • 多 Hermes 实例支持

Risks & Dependencies

风险 影响 缓解
sync_token 内存消费记录在多实例部署下失效 重复消费风险 后续可改用 Redis当前单实例够用
Mock Hermes 端点误暴露到生产环境 安全风险 NODE_ENV !== 'production' 注册路由
Hermes 回调时 EternalAI 不可达 同步失败 同步状态设为 failed管理员可重试
sync_token 在传输中被截获 5 分钟内可滥用 HTTPS + 一次性消费 + 短过期

Open Questions

  • OQ1: sync_token 一次性消费用内存 Set 还是数据库表?(计划用内存 Set单实例部署够用多实例时改 Redis
  • OQ2: 管理员账号初始创建方式?(计划用 prisma db seed 脚本,账号密码从环境变量读取)
  • OQ3: EternalAI 自身的基础 URLETERNALAI_BASE_URL)如何获取?(计划从 SystemConfig 读取,首次部署时配置)

System-Wide Impact

  • 数据库: 新增 Admin、SystemConfig 表Role 表新增 4 个字段
  • API: 新增 /api/admin-auth/api/admin/api/admin/config/api/admin/sync/api/mock-hermes 路由组
  • 前端: 新增 3 个管理员视图,改造创作者角色卡片
  • 认证: 新增 Admin JWT独立 secret和 sync_tokenHMAC SHA-256两套机制
  • 向后兼容: 现有用户 API Key 拉取流程不变