本模块属于 LinkChat 主仓库。只提供 Web 管理界面,不需要定制 CLI。
生产地址:https://chat.linkchat.online/admin/,也支持 https://linkchat.online/admin/。使用已有管理员账号登录;普通用户不能访问管理接口。
功能
- 运行概况:用户、在线人数、频道、消息、有效 API Key、封禁用户、进行中游戏。
- 用户:搜索、封禁/解封、ADMIN / MEMBER 角色调整。禁止修改自己的角色或封禁自己;其他管理员必须先降为普通成员才能封禁。
- 频道:搜索、改名、查看成员、添加/移除成员、删除及关联清理。
- 消息:全站搜索、详情、单条或勾选批量撤回。保留引用关系并写入实时事件,清理提醒与表情、取消待发邮件;删除没有其他消息引用的服务端附件文件。
- API Key:查看归属、前缀、状态、到期时间并撤销;不展示凭据明文或认证哈希。
- Spire:查看房间、玩家、阶段、层数;结束游戏、删除已结束存档。账号积分和角色胜场只读,删除存档保留这些进度。
- 操作记录:记录管理操作、操作人、目标、参数及时间,与成功写入同事务提交。
角色变更注销目标用户会话;封禁同时撤销其 API Key,解封不恢复已撤销凭据。服务端仍必须使用支持封禁校验的本仓库后端,不能退回不识别 BANNED 角色的旧后端。
界面与筛选
界面延续 AstroPaper 的等宽字体、纸白/灰蓝主题、蓝/橙强调色和波浪导航。顶部提供概况、用户、Chat、Forum、API Key、Spire、操作记录七个入口;Chat 包含频道和消息,Forum 包含版块、帖子、回复和举报,API Key 和 Spire 也使用二级标签。列表固定展示业务字段,详情在右侧抽屉打开,手机使用折叠菜单和全屏详情。
搜索、筛选、分页保存在 URL hash,例如 /admin/#/messages?channel_id=1&status=normal,支持刷新和浏览器前进后退恢复。默认每页 30 条,可选 50 / 100 条。批量撤回与永久删除仅针对本页,刷新、翻页或改变条件会清空勾选。消息、帖子、回复支持批量永久删除;每批最多 100 条,整批校验、同事务提交。
频道成员通过昵称、账号或 ID 搜索后选择添加;直接在成员行移除。删除频道需要输入完整频道名称确认。用户角色使用下拉选择,并明确展示操作影响。
列表接口保持 rows / total / offset / limit 与 id / label / details / actions 结构,新增筛选如下:
| 资源 | 可选条件 |
|---|---|
| users | role=ADMIN/MEMBER/BANNED |
| channels | owner_id |
| messages | channel_id、user_id、status=normal/recalled、date_from、date_to |
| api-keys | user_id、status=valid/expired/revoked |
| spire | owner_id、status=active/finished |
| audit | actor_id、action(如 users.ban)、date_from、date_to |
各条件与 q 组合生效,服务端先筛选再统计及分页。不支持的条件或非法值返回 422。日期采用 YYYY-MM-DD 和已有服务端时间口径,结束日期包含全天。关联名称及成员数按页批量查询,关联用户缺失时保留 ID。概况与最近操作独立加载和重试。
API Key 治理与 API 调用记录
「API Key → API 调用」集中展示请求时间、API Key/所有人、操作、路径、来源 IP、状态码、结果、耗时和关联 ID。可按 API Key 类型/ID、用户、频道、房间、请求方式、结果、状态码、日期筛选;关键词搜索请求 ID、幂等键/动作 ID、连接 ID、接口、操作、摘要及名称。API Key 详情提供权限、授权范围、最近调用时间、保留期内记录数/异常数/限流数,并能直接打开该 API Key 的调用记录。
- HTTP:全量记录
/api/v1/请求(包括认证失败)及携带 API Key 的/api/请求;普通 Cookie/会话调用不进入 API Key 日志。 - WebSocket:记录机器 API Key 连接/断开、每个客户端动作和 SYNC 请求,按 actionId/requestId 匹配回执。相同动作 ID 的重试仍各自成条。PING/PONG 和服务端主动推送不作为调用;未收到回执的动作标记“结果未知”。
- HTTP Request ID 与响应
X-Request-ID一致;幂等键、动作/批次 ID 单独保存,可追踪重复请求。批量动作提前停止显示“部分执行”;重启前未完成记录标记“结果未知”,不推断业务是否已经提交。 - 按管理员要求,详情按原始值保存查询参数、完整请求/响应头(包括认证信息)、请求体及返回结果,不做字段或文本脱敏。重复请求头会保留。列表不携带大正文,调用列表与详情都仅管理员可读。
- 请求/响应单侧默认上限 256 KiB;超过上限、未被处理器读取或连接中断都会标注原因和已观察字节数。正常 JSON 保留原始字段值;无效 JSON/文本按 UTF-8 原文展示,非 UTF-8 二进制以 Base64 保存。调用详情为历史快照。
- 默认保留 30 天,启动及每小时分批删除过期记录;查询立即排除超期记录。可通过
API_AUDIT_RETENTION_DAYS(1–3650)与API_AUDIT_BODY_LIMIT(4096–4194304 字节)调整。 - 新增
api_call_audit表,由服务启动幂等创建,既有业务表不变。历史调用不能追溯补录。日志写入使用独立事务,正常业务不会因日志写入失败被重复执行或回滚;采集异常会在服务器日志及调用页显示本次运行的失败计数,提示记录可能不完整。
管理员只读接口:GET /api/admin/api-calls、GET /api/admin/api-calls/{id}。列表保留标准分页结构并附带采集健康信息;详情增加 payloads,普通账号对两者均无权访问。
本地运行
按仓库根目录 README 配置并启动 FastAPI 后端,然后打开 http://127.0.0.1:8000/admin/。静态 HTML、CSS、JavaScript 和字体由 FastAPI 提供,无需前端构建。启用 Spire 管理需要安装 extensions/spire 并设置 LINKCHAT_GAMES_ENABLED=true。
后台沿用原账号体系,不开放管理员注册。全新环境仍按原项目规则将首个注册账号设为管理员;注册需要配置 SMTP。测试账号仅在隔离测试数据库中创建。
部署
前后端应同批发布,本轮启动会自动创建 api_call_audit 审计表,无需手工修改既有业务表。将完整 admin-web/(包括 config.js)与 linkchat-backend/ 放在同级目录,或通过 ADMIN_WEB_DIR 指定其路径。每个发布目录都必须包含 admin-web/:缺少时后端照常启动,但 /admin/ 返回 404,启动日志记录 admin web not mounted 警告。发布后检查 curl -sI https://chat.linkchat.online/admin/ 返回 200。保留原数据库、上传目录和 .env,通过 DATABASE_URL / UPLOAD_DIR 配置。启动仍只用一个 Uvicorn worker,避免游戏锁、连接和在线状态跨进程失效。启动自动增加 admin_audit 表。
Nginx 将 /admin/ 和 /api/ 转发到后端,模板见 linkchat-backend/deploy/nginx-linkchat.conf。正常聊天 Web 和 CLI 继续使用原接口,无需定制客户端。
当前服务器通过 /etc/systemd/system/linkchat-backend.service.d/zz-linkchat-admin.conf 从 /opt/linkchat-admin/current/linkchat-backend 加载包含后台的后端;这是运行时发布目录,源码归属本 LinkChat 仓库。工作目录、Python 环境、数据库和上传目录仍位于 /opt/linkchat-backend。最初上线备份保留在 /opt/linkchat-admin/backups/。
测试
从 LinkChat 仓库根目录执行:
cd linkchat-backend
DATABASE_URL=sqlite:// PYTHONPATH=../extensions/spire .venv/bin/python -B -m unittest discover -s tests -q
cd ..
npm install --prefix .qa --no-save playwright@1.64.1
LINKCHAT_TEST_PYTHON="$PWD/linkchat-backend/.venv/bin/python" bash scripts/test-web.sh
浏览器测试需要本机 Chrome,自动建立和清理临时 SQLite 数据库,验证普通账号拒绝访问、管理员登录、明暗主题、移动端布局、频道成员搜索/添加/移除和改名、删除确认、组合筛选、分页及历史恢复、批量撤回、角色变更、封禁/解封、Spire、进度、审计、抽屉焦点和退出。覆盖 1440、1024、768、390px 明暗主题,新增统一 Forum 创建、编辑、置顶锁帖、级联删除、批量删除确认、用户资料与账号删除、API Key 权限即时生效和消息编辑删除验证。并通过真实 Agent 请求验证 API 调用筛选、详情、原始值与 API Key 跳转。截图保存在忽略目录 .qa/screenshots/。
Chat / Forum 统一管理与数据库级操作
论坛已并入 /admin/#/forum-boards,与 Chat 共用登录、导航、筛选分页、详情抽屉、编辑确认和操作记录。旧 /admin/forum.html 自动跳转到统一后台。已有 /api/admin/forum/* 接口保留兼容。
| 资源 | 操作 |
|---|---|
| 用户 | 编辑昵称及本站头像路径;封禁、解封、调整角色;永久删除账号 |
| 频道 | 编辑名称和说明、转移所有者、管理成员、删除频道及消息 |
| 消息 | 编辑正文、撤回、单条或批量永久删除 |
| 论坛版块 | 创建、编辑名称/说明/排序、归档;永久删除版块及所有帖子回复 |
| 论坛帖子 | 编辑标题、正文、所属版块;置顶、锁定、软删除、单条或批量永久删除 |
| 论坛回复 | 编辑正文、软删除、单条或批量永久删除 |
| 举报 | 分页筛选、定位帖子或回复、处理或驳回并填写说明 |
| API Key | 修改名称、到期时间、六项权限;撤销。已撤销的 Key 不能恢复授权 |
永久删除必须输入目标完整 ID;批量删除输入 DELETE 数量,服务端再次验证。编辑只允许对应表单字段,不能通过附加字段改变作者、主键或凭据。普通账号和机器 API Key 无管理员访问权。所有修改与审计记录在同一事务提交,操作成功前不会删除磁盘文件。
消息永久删除会清理表情、提醒、邮件和投递记录,并保留发送回执墓碑以避免重试重复发送。实时推送与断线补齐将删除标记转换成现有客户端支持的撤回占位,不保存原正文。服务器附件只有在没有其他消息引用时才删除。论坛永久删除会清理点赞、收藏、关注、提醒、举报和关联附件;其他回复保留并解除对被删回复的引用,发布幂等回执作为墓碑保留。软删除内容仍可由管理员检查。
删除用户时,先转移其所有频道,并退出或结束其参与的进行中游戏。不能删除自己或其他管理员;其他管理员须先降级。账号、登录会话、API Key、频道成员关系、私人通知和互动关系会被删除;历史发言、游戏存档和审计记录继续保留,作者 ID 作为历史标识,发言显示“已删除用户”。已有 Chat 和游戏连接会断开。此操作不是对历史内容、附件及审计快照的隐私擦除。
统一列表接口为 GET /api/admin/forum-boards|forum-posts|forum-replies|forum-reports,沿用 rows / total / offset / limit。帖子支持 board_id / user_id / status / date_from / date_to,回复与举报支持 post_id / user_id / status / date_from / date_to,版块支持 status=active/archived。内容状态为 normal/deleted;举报为 open/resolved/dismissed。
写入沿用 POST /api/admin/{resource}/{id}/{operation},参数为 values;批量目标用 bulk 和 ids。新增操作包括 edit / purge / transfer / create / hide / pin / unpin / lock / unlock / resolve,由资源限定。创建版块使用 forum-boards/new/create。purge 的 values.confirmation 必须与目标 ID 或 DELETE 数量 一致。新增界面无需构建,后端无新增表或列;应将 admin-web/ 和后端同批更新。