Files
planet/docs/backend/system-service-control.md
2026-04-20 15:14:53 +08:00

9.6 KiB

System Service Control

This document defines the fixed mapping between admin control-plane actions and the existing planet.sh service-management commands.

The goal is to reuse the current operational script semantics without exposing arbitrary shell execution to the frontend or API callers.

Scope

  • This mapping is for admin-side operational controls only.
  • The control plane must submit a fixed action name, not a raw shell command.
  • The backend is responsible for translating an allowed action into a fixed planet.sh invocation.

Design Rules

  • Only whitelist actions may be executed.
  • The frontend must never send arbitrary shell strings.
  • The backend must build command arguments from a fixed mapping table.
  • High-risk actions should be restricted to super_admin.
  • Prefer partial restarts over full-stack restarts when UI continuity matters.

Action Mapping

Action name Intended use planet.sh command Notes
restart-backend Restart backend API only ./planet.sh restart -b Recommended first implementation for UI-triggered restart flows.
restart-database Restart PostgreSQL and Redis containers ./planet.sh restart -d Useful when database/cache services need a controlled bounce without restarting the UI.
restart-system Restart the whole application stack ./planet.sh restart Frontend continuity breaks briefly; UI should switch to guided recovery mode.
restart-frontend Restart frontend dev server only ./planet.sh restart -f Use with caution; UI continuity is weaker than backend-only restart.
restart-backend-port Restart backend on a specific port ./planet.sh restart -b <port> Port must be backend-validated before execution.
restart-frontend-port Restart frontend on a specific port ./planet.sh restart -f <port> Port must be backend-validated before execution.
health-check Read current service health ./planet.sh health Safe read-only operational action.
show-logs-backend Inspect backend logs ./planet.sh log -b Best used for CLI/operator tooling, not normal Web UI streaming.
show-logs-frontend Inspect frontend logs ./planet.sh log -f Best used for CLI/operator tooling, not normal Web UI streaming.

Not Exposed In UI By Default

The following existing script capabilities should not be exposed directly in the Web UI unless there is an explicit product need and an additional safety review:

  • ./planet.sh restart
  • ./planet.sh start
  • ./planet.sh stop
  • ./planet.sh createuser
  • any future raw shell passthrough

Reason:

  • full restart can break the current control session;
  • stop/start have larger blast radius;
  • user creation is not a service-control operation;
  • raw shell passthrough creates unnecessary privilege risk.

Frontend action payload

{
  "action": "restart-backend"
}

Backend command resolution

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 Draft

Primary Endpoint

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

Purpose:

  • create a controlled restart task;
  • resolve a whitelist action into a fixed planet.sh command;
  • hand execution off to an external runner or detached subprocess.

Request Body

{
  "action": "restart-backend"
}

Optional future shape:

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

Response

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

Task Query Endpoint

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

Response shape:

{
  "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"
}

Optional Log Endpoint

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

Suggested response:

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

This log endpoint is optional for phase one. The first version can work with task state plus /health polling alone.

Task State Model

Status

  • queued
  • running
  • succeeded
  • failed
  • timeout

Stage

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

Interpretation

  • status is the high-level terminal or non-terminal state.
  • stage is the operator-facing execution phase for the UI.
  • message is the short human-readable line shown in the modal or full-screen overlay.

Permission Model

  • restart-backend should require super_admin.
  • Permission checks should follow the same role pattern already used in users.py.
  • Frontend visibility may hide controls for non-super_admin, but backend must still enforce authorization.

Storage Model

Recommended first implementation:

  • store restart task state in Redis;
  • keep task lifetime short;
  • keep recent logs as a bounded list.

Suggested keys:

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

Suggested stored fields:

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

Execution Model

The request-handling API process should not depend on itself surviving long enough to stream the whole restart output.

Recommended execution flow:

  1. validate caller and action
  2. create task state in Redis
  3. resolve action to fixed planet.sh argv
  4. spawn detached executor
  5. return task_id
  6. executor updates task state while restart is in progress
  7. frontend polls health and/or task state until recovery

Recommended command resolution examples:

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"]

Frontend Polling Flow

Recommended first-phase UX:

  1. user clicks 重启后端
  2. confirmation modal explains temporary unavailability
  3. frontend calls POST /api/v1/system/restart-tasks
  4. UI enters blocking restart state
  5. frontend polls /health every 1-2s
  6. temporary request failures are treated as expected
  7. after 2-3 consecutive successful health checks, frontend reloads page

Optional richer polling:

  1. poll task status endpoint while backend is still reachable
  2. switch to /health recovery polling after disconnect begins
  3. refresh page after health recovery

Frontend State Machine

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

Suggested UI messages:

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

Phase-One Recommendation

Implement only the following in phase one:

  • restart-backend
  • super_admin permission gate
  • task creation endpoint
  • Redis-backed task state
  • frontend confirmation modal
  • frontend /health polling
  • automatic page reload after recovery

Do not implement in phase one:

  • full ./planet.sh restart
  • raw shell command passthrough
  • arbitrary service control
  • full terminal stdout streaming
  • multi-action concurrent restart queueing

Implementation Checklist

Backend

  1. add a dedicated system-control API module under backend/app/api/v1/
  2. add a whitelist-based action resolver for planet.sh
  3. store restart task state in Redis
  4. add detached restart-runner script execution
  5. expose:
    • POST /api/v1/system/restart-tasks
    • GET /api/v1/system/restart-tasks/{task_id}
    • optional task log endpoint
  6. enforce super_admin permission on all restart-task endpoints

Frontend

  1. add a 重启后端 control on the dashboard for super_admin
  2. show a confirmation modal before dispatch
  3. after submission, switch modal into blocking restart state
  4. poll /health until backend recovery is confirmed
  5. auto-refresh page after consecutive successful health checks
  6. show short stage-oriented logs instead of raw terminal streaming

Operational Notes

  1. phase one should target backend-only restart
  2. frontend restart should remain out of scope initially
  3. command execution must always originate from repository root
  4. only fixed action names may cross the API boundary

Validation Requirements

  • Reject any action not present in the whitelist.
  • If a port-bearing action is added, validate the port as an integer in 1..65535.
  • Resolve commands from the repository root so planet.sh runs with a stable working directory.
  • Record the requested action, operator identity, execution start time, and result.

Implementation Guidance

  • For UI-triggered restart flows, prefer restart-backend first.
  • Do not rely on the current API request process to stream full restart output after it triggers its own restart.
  • Use a task record plus polling/health-check recovery flow instead of raw terminal streaming as the primary UX.