Files
Genarrative/docs/technical/ADMIN_CONSOLE_SERVICE_DESIGN_2026-04-23.md
kdletters 6bc591b1f4
Some checks failed
CI / verify (push) Has been cancelled
Fix admin table overview permissions
2026-05-01 19:46:50 +08:00

8.5 KiB
Raw Permalink Blame History

后台管理服务设计

日期:2026-04-23

更新:2026-04-30

状态说明:本文件中的管理员鉴权、/admin/api/* 管理接口、数据库概览与受控 API 调试设计继续有效;同源内嵌 HTML/CSS/JS 后台页面已废弃。后续后台 UI 迁移到独立前端工程,当前 api-server 不再挂载 GET /admin 页面入口。独立后台前端的产品边界见 ../prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md,技术方案见 ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md

1. 目标

为当前 Rust api-server 增加一套同源后台管理服务,满足以下首版目标:

  1. 支持管理员用户名密码登录。
  2. 支持独立的管理员鉴权,不允许普通玩家 JWT 越权访问。
  3. 支持在后台查看当前服务与数据库概览信息。
  4. 支持在后台测试当前 api-server 已挂载接口。
  5. 保持管理能力继续收口在 server-rs,管理 UI 由独立后台前端工程承接。

2. 背景与约束

当前仓库已具备:

  1. Rust api-server 主链。
  2. 基于 JWT + refresh session 的普通用户登录体系。
  3. SpacetimeDB + spacetime-client 的主数据面。

本次后台管理服务必须继续遵守:

  1. 后端统一落在 server-rs,不回退到 server-node
  2. 不额外新起独立管理服务进程。
  3. 管理 API 继续作为受保护管理域挂载在 api-server
  4. 数据库信息必须尽量读取真实数据库侧信息,不能只展示硬编码假数据。

3. 首版范围

3.1 包含

  1. POST /admin/api/login:管理员用户名密码登录。
  2. GET /admin/api/me:当前管理员会话信息。
  3. GET /admin/api/overview:服务与数据库概览。
  4. POST /admin/api/debug/http:受控 HTTP 接口调试。
  5. POST /admin/api/profile/redeem-codes:兑换码创建/更新。
  6. POST /admin/api/profile/redeem-codes/disable:兑换码停用。
  7. 基于 Bearer JWT 的管理员鉴权中间件。

3.2 不包含

  1. 多角色管理员体系。
  2. 管理员 refresh cookie / 多端会话管理。
  3. 后台直接写库、删库、执行 reducer。
  4. 任意 SQL 执行器。
  5. api-server 内嵌 HTML/CSS/JS 后台页面。

4. 总体方案

4.1 部署形态

后台管理服务直接挂载在现有 server-rs/crates/api-server 内,作为同一个 Axum 进程的一部分。

原因:

  1. 当前 api-server 已具备配置、JWT、错误包裹、日志与同源路由能力。
  2. 后台本质上是服务运维与调试面,不值得单独再起一个网关或 BFF。
  3. 同源可以避免开发期额外 CORS 和 cookie 域问题。

4.2 页面形态

后台管理页面不再由 api-server 直接返回内嵌 HTML/CSS/JS。api-server 仅保留管理 API页面由独立后台前端工程调用这些接口。

原因:

  1. 管理 UI 需要独立演进,不应继续堆在 Rust 源码字符串中。
  2. server-rs 继续负责鉴权、聚合和写操作,符合前端只做表现的工程约束。
  3. 删除 GET /admin 后,当前服务访问该路径应返回 404

4.3 数据库信息来源

数据库概览不走本地 CLI shell也不依赖前端直接访问数据库。

首版采用两类信息源:

  1. 服务端配置与连接信息:来自 api-server 当前 AppConfig
  2. SpacetimeDB 真正的数据库元信息与表行数:由 api-server 通过 SpacetimeDB 官方 HTTP API 读取。

读取口径:

  1. /v1/database/{database}:读取数据库基础信息。
  2. /v1/database/{database}/schema:读取 schema 信息。
  3. /v1/database/{database}/sql:对受控表执行 SELECT COUNT(*) 统计。

说明:

  1. 首版只做只读概览,不暴露任意 SQL 输入。
  2. 表清单由后端显式维护,避免用户在后台拼接任意查询。

5. 管理员鉴权设计

5.1 管理员账号来源

首版不复用普通玩家账号仓储,不把管理员账号混进 module-auth 用户表。

管理员账号来自环境变量:

  1. GENARRATIVE_ADMIN_USERNAME
  2. GENARRATIVE_ADMIN_PASSWORD

原因:

  1. 管理员是平台运维身份,不等于玩家账号。
  2. 首版目标是尽快落地可靠后台,不引入额外管理员表迁移。
  3. 环境变量方案最适合当前阶段的单后台入口。

5.2 管理员 JWT

后台登录成功后签发独立管理员 Bearer JWT。

claims 设计:

  1. 继续复用 platform-auth::AccessTokenClaims
  2. roles 固定包含 admin
  3. sub 使用稳定管理员主体,例如 admin:<username>
  4. sid 使用后台会话 ID。
  5. 不写 refresh cookie。

5.3 权限校验

新增 require_admin_auth 中间件,校验规则如下:

  1. Bearer token 必须可被当前 JWT 配置正确验签。
  2. roles 中必须包含 admin
  3. sub 必须匹配当前管理员配置主体。

普通用户 token 即使同样由本服务签发,只要不带 admin 角色,也一律拒绝访问后台接口。

6. 后台页面设计

本节已由独立后台前端工程方案接管。历史同源页面曾包含三个主区域:

  1. 登录卡片。
  2. 数据库概览面板。
  3. API 调试面板。

交互原则:

  1. 页面简洁,不默认塞说明性长文案。
  2. 移动端优先,窄屏下卡片改纵向堆叠。
  3. API 调试结果在独立结果面板展示,不在按钮下方临时插一段文本。

7. 数据库概览设计

GET /admin/api/overview 返回以下信息:

  1. 当前服务监听信息。
  2. 当前 SpacetimeDB server/database 配置。
  3. SpacetimeDB 数据库基础信息。
  4. 当前 schema 表清单。
  5. schema 表清单对应的逐表行数统计。

表统计必须以 SpacetimeDB schema 返回的表名为唯一来源,schemaTableNames 的数量必须与 tableStats 的行数一致。后台服务只对 schema 中符合安全 SQL 标识符格式的表名发起 SELECT COUNT(*),不提供任意 SQL 输入能力。

返回中的计数失败项必须带错误信息不能静默吞掉。SpacetimeDB private 表或当前身份不可见的表可能在 /sql 下返回 no such table / marked private这类项统一展示为“不可统计private 或当前身份不可见)”,不作为整页读取失败处理。

8. API 调试设计

POST /admin/api/debug/http 提供一个受控 HTTP 调试代理。

请求参数:

  1. method
  2. path
  3. headers
  4. body

限制:

  1. 只允许访问当前服务同源相对路径。
  2. 调试回环地址由服务端按当前 bind_host 解析;若服务监听在 0.0.0.0::,后台自动改走 loopback避免把通配监听地址直接当成调试目标。
  3. 禁止调 /admin/api/login 本身,避免自套娃。
  4. 禁止覆盖 hostcontent-length 等危险头。
  5. 请求超时固定收口。
  6. 返回调试结果时回显状态码、响应头、响应文本预览。

该能力用于验证当前服务端接口,不等价于通用代理工具。

9. 配置项

新增以下环境变量:

  1. GENARRATIVE_ADMIN_USERNAME
  2. GENARRATIVE_ADMIN_PASSWORD
  3. GENARRATIVE_ADMIN_TOKEN_TTL_SECONDS

默认策略:

  1. 若未配置用户名或密码,则后台登录接口返回 503,独立后台前端自行展示未启用状态。
  2. 默认管理员 token TTL 为 4 小时。

10. 测试要求

至少覆盖:

  1. 管理员登录成功。
  2. 管理员密码错误返回 401
  3. 普通用户 token 访问后台接口返回 403
  4. 未登录访问后台接口返回 401
  5. 后台概览接口在未启用管理员配置时返回 503
  6. API 调试接口能成功访问 /healthz
  7. API 调试接口拒绝绝对 URL 和后台自身登录接口。

11. 路由清单

当前保留的管理 API 路由:

  1. POST /admin/api/login
  2. GET /admin/api/me
  3. GET /admin/api/overview
  4. POST /admin/api/debug/http
  5. POST /admin/api/profile/redeem-codes
  6. POST /admin/api/profile/redeem-codes/disable

GET /admin 已取消挂载,后续由独立后台前端工程承接页面入口。

12. 完成定义

当前管理 API 保留与内嵌页面移除满足以下条件时,本任务视为完成:

  1. api-server 内存在受保护后台管理域。
  2. 管理员用户名密码可登录。
  3. 普通用户 token 无法访问后台接口。
  4. 后台能看到服务和数据库真实概览。
  5. 后台能调试当前服务 HTTP 接口。
  6. 兑换码管理 API 可由管理员 token 调用。
  7. GET /admin 不再挂载,访问返回 404
  8. 独立后台前端 PRD 与技术方案已补齐。
  9. 路由索引与技术文档已同步更新。