348 lines
9.6 KiB
Markdown
348 lines
9.6 KiB
Markdown
# 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.
|
|
|
|
## Recommended First-Phase UI Contract
|
|
|
|
### Frontend action payload
|
|
|
|
```json
|
|
{
|
|
"action": "restart-backend"
|
|
}
|
|
```
|
|
|
|
### Backend command resolution
|
|
|
|
```text
|
|
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
|
|
|
|
```json
|
|
{
|
|
"action": "restart-backend"
|
|
}
|
|
```
|
|
|
|
Optional future shape:
|
|
|
|
```json
|
|
{
|
|
"action": "restart-backend-port",
|
|
"port": 8000
|
|
}
|
|
```
|
|
|
|
### Response
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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](/home/ray/dev/linkong/planet/backend/app/api/v1/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:
|
|
|
|
```text
|
|
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.
|