Files
planet/docs/technical/zh/backend-system-service-control.md
2026-04-29 17:27:44 +08:00

8.7 KiB
Raw Blame History

系统服务控制

本文定义后台控制面动作与现有 planet.sh 服务管理命令之间的固定映射。

目标是在复用当前运维脚本语义的同时,不向前端或 API 调用方暴露任意 shell 执行能力。

范围

  • 这套映射只用于管理端运维控制。
  • 控制面必须提交固定 action 名称,而不是原始 shell 命令。
  • 后端负责把允许的 action 翻译成固定的 planet.sh 调用。

设计规则

  • 只允许执行白名单 action。
  • 前端绝不能发送任意 shell 字符串。
  • 后端必须从固定映射表构造命令参数。
  • 高风险 action 应限制为 super_admin
  • 在 UI 连续性重要时,优先局部重启,而不是全栈重启。

Action 映射

Action 名称 用途 planet.sh 命令 备注
restart-backend 只重启后端 API ./planet.sh restart -b 页面通常短暂失联后由 /health 轮询恢复。
restart-frontend 只重启前端开发服务器 ./planet.sh restart -f 页面入口会短暂不可用UI 通过前端入口探测恢复后刷新。
restart-database 重启 PostgreSQL 和 Redis 容器 ./planet.sh restart -d 适合数据库/缓存需要受控重启但不希望重启 UI 的场景。
restart-system 重启整个应用栈 ./planet.sh restart 前端会短暂中断UI 应进入引导恢复模式。
restart-backend-port 在指定端口重启后端 ./planet.sh restart -b <port> 执行前必须由后端校验端口。
restart-frontend-port 在指定端口重启前端 ./planet.sh restart -f <port> 执行前必须由后端校验端口。
health-check 读取当前服务健康状态 ./planet.sh health 安全的只读运维动作。
show-logs-backend 查看后端日志 ./planet.sh log -b 更适合 CLI/运维工具,不建议作为普通 Web UI 日志流。
show-logs-frontend 查看前端日志 ./planet.sh log -f 更适合 CLI/运维工具,不建议作为普通 Web UI 日志流。

默认不暴露到 UI 的能力

除非有明确产品需求并经过额外安全评审,否则以下脚本能力不应直接暴露到 Web UI

  • ./planet.sh restart
  • ./planet.sh start
  • ./planet.sh stop
  • ./planet.sh createuser
  • 任何未来的原始 shell 透传能力

原因:

  • 全量重启可能打断当前控制会话;
  • stop/start 影响面更大;
  • 用户创建不是服务控制操作;
  • 原始 shell 透传会引入不必要的权限风险。

第一阶段推荐 UI 契约

前端 action payload

{
  "action": "restart-backend"
}

后端命令解析

restart-backend -> ["./planet.sh", "restart", "-b"]
restart-database -> ["./planet.sh", "restart", "-d"]
restart-system -> ["./planet.sh", "restart"]
restart-frontend -> ["./planet.sh", "restart", "-f"]
health-check -> ["./planet.sh", "health"]

API 草案

主接口

  • POST /api/v1/system/restart-tasks

用途:

  • 创建受控重启任务;
  • 将白名单 action 解析成固定 planet.sh 命令;
  • 把执行交给外部 runner 或 detached subprocess。

请求体

{
  "action": "restart-backend"
}

未来可选形态:

{
  "action": "restart-backend-port",
  "port": 8000
}

响应

{
  "task_id": "restart_20260331_153000_ab12cd",
  "action": "restart-backend",
  "status": "queued",
  "stage": "accepted",
  "message": "Restart task accepted"
}

任务查询接口

  • GET /api/v1/system/restart-tasks/{task_id}

响应结构:

{
  "task_id": "restart_20260331_153000_ab12cd",
  "action": "restart-backend",
  "status": "queued",
  "stage": "accepted",
  "message": "Waiting for execution",
  "requested_by": {
    "id": 1,
    "username": "admin"
  },
  "created_at": "2026-03-31T15:30:00+08:00",
  "updated_at": "2026-03-31T15:30:02+08:00"
}

可选日志接口

  • GET /api/v1/system/restart-tasks/{task_id}/logs

建议响应:

{
  "task_id": "restart_20260331_153000_ab12cd",
  "lines": [
    "accepted restart-backend request",
    "spawning restart command",
    "waiting for backend shutdown",
    "waiting for backend health recovery"
  ]
}

日志接口在第一阶段不是必需项。首版可以只依赖任务状态加 /health 轮询。

任务状态模型

Status

  • queued
  • running
  • succeeded
  • failed
  • timeout

Stage

  • accepted
  • spawning
  • stopping
  • starting
  • waiting_for_health
  • healthy
  • failed

含义

  • status 是高层终态/非终态状态。
  • stage 是面向运维人员和 UI 的执行阶段。
  • message 是 modal 或全屏遮罩中展示的短文本。

权限模型

  • restart-backend 应要求 super_admin
  • 权限检查应沿用 users.py 中已有的角色模式。
  • 前端可以对非 super_admin 隐藏控件,但后端必须继续强制鉴权。

存储模型

推荐第一阶段实现:

  • 将重启任务状态存入 Redis
  • 任务生命周期保持较短;
  • 最近日志用有界列表保存。

建议 key

  • system:restart_task:{task_id}
  • system:restart_task:{task_id}:logs

建议字段:

  • task_id
  • action
  • status
  • stage
  • message
  • requested_by_id
  • requested_by_username
  • created_at
  • updated_at

执行模型

处理请求的 API 进程不应依赖自身持续存活来流式输出完整重启日志。

推荐执行流程:

  1. 校验调用方和 action
  2. 在 Redis 中创建任务状态
  3. 将 action 解析为固定 planet.sh argv
  4. 启动 detached executor
  5. 返回 task_id
  6. executor 在重启过程中更新任务状态
  7. 前端轮询健康状态和/或任务状态,直到服务恢复

推荐命令解析示例:

restart-backend      -> ["./planet.sh", "restart", "-b"]
restart-frontend     -> ["./planet.sh", "restart", "-f"]
restart-backend-port -> ["./planet.sh", "restart", "-b", "<port>"]
health-check         -> ["./planet.sh", "health"]

前端轮询流程

推荐第一阶段 UX

  1. 用户点击 重启后端
  2. 确认 modal 说明服务会短暂不可用
  3. 前端调用 POST /api/v1/system/restart-tasks
  4. UI 进入阻塞式重启状态
  5. 前端每 1-2s 轮询 /health
  6. 临时请求失败视为预期现象
  7. 连续 2-3 次健康检查成功后,前端刷新页面

可选增强轮询:

  1. 后端仍可达时轮询任务状态接口
  2. 断连开始后切换为 /health 恢复轮询
  3. 健康恢复后刷新页面

前端状态机

  • idle
  • confirming
  • submitting
  • waiting_for_shutdown
  • waiting_for_recovery
  • recovered
  • failed
  • timeout

建议 UI 文案:

  • 已发送重启指令
  • 正在停止后端服务
  • 正在等待服务恢复
  • 服务已恢复,正在刷新页面
  • 恢复超时,请手动检查服务状态

当前 Dashboard 实现

Dashboard 当前已实现:

  • restart-backend
  • restart-frontend
  • restart-ai-provider
  • restart-database
  • restart-system
  • super_admin 权限门禁
  • 任务创建接口
  • Redis 任务状态
  • 前端确认 modal
  • 后端 /health 轮询
  • 前端入口轮询
  • 恢复后自动刷新页面

暂不实现:

  • 原始 shell 命令透传
  • 任意服务控制
  • 完整终端 stdout 流式输出
  • 多 action 并发重启队列

实现清单

后端

  1. backend/app/api/v1/ 下新增专用系统控制 API 模块
  2. 增加基于白名单的 planet.sh action 解析器
  3. 将重启任务状态存入 Redis
  4. 增加 detached restart-runner 脚本执行
  5. 暴露:
    • POST /api/v1/system/restart-tasks
    • GET /api/v1/system/restart-tasks/{task_id}
    • 可选任务日志接口
  6. 对所有 restart-task 接口强制 super_admin 权限

前端

  1. 在 dashboard 为 super_admin 增加 重启服务 控件
  2. 发送前展示确认 modal
  3. 提交后将 modal 切换为阻塞式重启状态
  4. 后端重启使用 /health 轮询确认恢复
  5. 前端重启和完全重启使用前端入口探测确认恢复
  6. 连续健康检查成功后自动刷新页面
  7. 展示简短阶段日志,而不是原始终端流

运维说明

  1. 优先使用局部重启,只有确实需要时才执行完全重启
  2. 前端重启会打断当前页面入口,必须进入恢复等待状态
  3. 命令执行必须始终从仓库根目录发起
  4. API 边界只能传递固定 action 名称

校验要求

  • 拒绝任何不在白名单中的 action。
  • 如果增加带端口 action端口必须校验为 1..65535 的整数。
  • 从仓库根目录解析命令,确保 planet.sh 的工作目录稳定。
  • detached runner 使用 zsh -ic 执行白名单命令,确保 ~/.zshrc 中的本地环境变量进入重启流程。
  • 记录请求 action、操作者身份、执行开始时间和结果。