# API ## Purpose This document defines the MVP REST API for the server panel backend. ## Conventions - Base path: `/api/v1` - Authentication: `Bearer` token - Response format: JSON - Time format: ISO 8601 UTC - Errors should return a stable `code` and human-readable `message` - Error responses also include `request_id` for log correlation ## Authentication ## POST `/auth/login` Authenticate a user and return access and refresh tokens. Request: ```json { "username": "admin", "password": "strong-password" } ``` Response: ```json { "access_token": "jwt-access-token", "refresh_token": "jwt-refresh-token", "token_type": "bearer", "user": { "id": 1, "username": "admin", "role": "admin" } } ``` ## POST `/auth/refresh` Refresh the access token. Request: ```json { "refresh_token": "jwt-refresh-token" } ``` Response: ```json { "access_token": "new-access-token", "token_type": "bearer" } ``` ## POST `/auth/logout` Invalidate the current session by bumping the user's token version. Response: ```json { "success": true } ``` Notes: - invalidates both the current access token and refresh token family for the user; - password or role changes also invalidate older tokens. ## GET `/auth/me` Return the current authenticated user. Response: ```json { "id": 1, "username": "admin", "role": "admin", "is_active": true } ``` ## Users ## GET `/users` Return user list. Admin only. Response: ```json [ { "id": 1, "username": "admin", "role": "admin", "is_active": true } ] ``` ## POST `/users` Create a user. Admin only. Request: ```json { "username": "operator1", "password": "strong-password", "role": "operator" } ``` ## PATCH `/users/{user_id}` Update a user role or active flag. Admin only. Request: ```json { "role": "viewer", "is_active": true } ``` ## Servers ## GET `/servers` Return all configured servers visible to the current user. Response: ```json [ { "id": 1, "name": "prod-app-1", "host": "10.0.0.10", "port": 22, "ssh_username": "deploy", "environment": "production", "is_active": true } ] ``` ## POST `/servers` Create a server definition. Admin only. Request: ```json { "name": "prod-app-1", "host": "10.0.0.10", "port": 22, "ssh_username": "deploy", "connection_type": "ssh", "environment": "production", "is_active": true } ``` ## GET `/servers/{server_id}` Return server details. Response: ```json { "id": 1, "name": "prod-app-1", "host": "10.0.0.10", "port": 22, "ssh_username": "deploy", "connection_type": "ssh", "environment": "production", "is_active": true } ``` ## PATCH `/servers/{server_id}` Update server metadata. Admin only. Request: ```json { "name": "prod-app-main", "host": "10.0.0.11", "port": 2222, "ssh_username": "ops", "environment": "staging", "is_active": true } ``` ## DELETE `/servers/{server_id}` Soft-delete or deactivate a server. Admin only. Response: ```json { "success": true } ``` ## GET `/servers/{server_id}/health` Return basic reachability and health information. Response: ```json { "server_id": 1, "reachable": true, "status": "ok", "checked_at": "2026-06-07T00:00:00Z" } ``` ## GET `/servers/{server_id}/metrics` Return current server metrics. Notes: - currently implemented for `ssh` servers only; - requires `ssh_username` to be configured on the server record. Response: ```json { "server_id": 1, "cpu_percent": 32.5, "memory_percent": 61.2, "disk_percent": 48.9, "uptime_seconds": 345600, "load_average": [0.55, 0.72, 0.80], "collected_at": "2026-06-07T00:00:00Z" } ``` ## Managed Services ## POST `/servers/{server_id}/services` Create a managed service definition for a server. Admin only. Request: ```json { "name": "nginx", "service_type": "systemd", "control_enabled": true, "log_source": "file:/var/log/nginx/error.log" } ``` ## GET `/servers/{server_id}/services` Return all managed services for a server. Notes: - status is resolved at request time; - if the remote check fails, status may be `unknown` with a detail message. Response: ```json [ { "id": 1, "server_id": 1, "name": "nginx", "service_type": "systemd", "status": "running", "control_enabled": true, "log_source": null, "created_at": "2026-06-07T00:00:00Z", "last_checked_at": "2026-06-07T00:00:00Z" }, { "id": 2, "server_id": 1, "name": "app", "service_type": "docker", "status": "running", "control_enabled": true, "log_source": null, "created_at": "2026-06-07T00:00:00Z", "last_checked_at": "2026-06-07T00:00:00Z" } ] ``` ## GET `/servers/{server_id}/services/{service_name}` Return details for a managed service. Response: ```json { "id": 1, "server_id": 1, "name": "nginx", "service_type": "systemd", "status": "running", "control_enabled": true, "log_source": null, "created_at": "2026-06-07T00:00:00Z", "last_checked_at": "2026-06-07T00:00:00Z" } ``` ## PATCH `/servers/{server_id}/services/{service_name}` Update a managed service definition. Admin only. Request: ```json { "name": "nginx-edge", "service_type": "docker", "control_enabled": false, "log_source": "file:/var/log/nginx/error.log" } ``` ## POST `/servers/{server_id}/services/{service_name}/start` Queue a service start action. Current implementation note: - the system already creates action and audit records; - execution is queued and completed by the worker process. Response: ```json { "id": 101, "server_id": 1, "service_id": 1, "target_type": "service", "target_name": "nginx", "action": "start", "status": "pending", "source": "web", "requested_by": 1, "result_message": null, "requested_at": "2026-06-07T00:00:00Z", "completed_at": null } ``` ## POST `/servers/{server_id}/services/{service_name}/stop` Queue a service stop action. Response: ```json { "id": 102, "status": "pending" } ``` ## POST `/servers/{server_id}/services/{service_name}/restart` Queue a service restart action. Response: ```json { "id": 103, "status": "pending" } ``` ## Logs ## GET `/servers/{server_id}/logs` Return available log sources for the server. Response: ```json { "server_id": 1, "sources": [ "nginx", "app" ] } ``` ## GET `/servers/{server_id}/logs/{service_name}` Return recent log lines. Query params: - `lines`: integer, optional, default `100`, max `500` Notes: - currently implemented for `ssh` servers only; - if `log_source` is set, it must use the format `file:/absolute/path`; - otherwise logs come from `journalctl` for `systemd` services or `docker logs` for Docker services. Response: ```json { "server_id": 1, "service_name": "nginx", "source_type": "file", "lines": [ "2026-06-07T00:00:01Z info service started", "2026-06-07T00:00:03Z info ready" ], "retrieved_at": "2026-06-07T00:00:10Z" } ``` ## Actions ## POST `/actions` Generic action endpoint if the frontend later uses a unified action form. Request: ```json { "server_id": 1, "target_type": "service", "target_name": "nginx", "action": "restart" } ``` Response: ```json { "action_id": 104, "status": "pending" } ``` ## GET `/actions` Return recent actions with optional filters. Suggested query params: - `server_id` - `status` - `requested_by` - `limit` Response: ```json [ { "id": 104, "server_id": 1, "service_id": 1, "target_type": "service", "target_name": "nginx", "action": "restart", "status": "success", "requested_by": 1, "source": "web", "requested_at": "2026-06-07T00:00:00Z", "completed_at": "2026-06-07T00:00:04Z" } ] ``` ## GET `/actions/{action_id}` Return details for a single action. Response: ```json { "id": 104, "server_id": 1, "target_type": "service", "target_name": "nginx", "action": "restart", "status": "success", "source": "web", "requested_by": 1, "result_message": "service restarted successfully", "requested_at": "2026-06-07T00:00:00Z", "completed_at": "2026-06-07T00:00:04Z" } ``` ## Audit ## GET `/audit` Return audit records. Admin only by default. Suggested query params: - `actor_id` - `target_type` - `action` - `limit` Response: ```json [ { "id": 1001, "actor_id": 1, "actor_source": "web", "target_type": "service", "target_id": "nginx", "action": "restart", "result": "success", "details": { "server_id": 1, "action_id": 104, "message": "service restarted successfully" }, "created_at": "2026-06-07T00:00:04Z" } ] ``` ## GET `/audit/{audit_id}` Return one audit record. Response: ```json { "id": 1001, "actor_id": 1, "actor_source": "telegram", "target_type": "service", "target_id": "nginx", "action": "restart", "result": "success", "details": { "server_id": 1 }, "created_at": "2026-06-07T00:00:04Z" } ``` ## Telegram ## GET `/telegram/status` Return backend Telegram integration status. Admin only. Response: ```json { "enabled": true, "mode": "polling", "linked_users_count": 2 } ``` ## POST `/telegram/link` Issue a one-time Telegram link code for the authenticated user. Response: ```json { "link_code": "ABC123DEF456", "expires_at": "2026-06-07T00:10:00Z" } ``` ## GET `/telegram/link/me` Return the current user's Telegram link if one exists. Response: ```json { "id": 1, "user_id": 1, "telegram_user_id": "123456789", "chat_id": "123456789", "linked_at": "2026-06-07T00:05:00Z", "created_at": "2026-06-07T00:00:00Z" } ``` If no link exists yet, the endpoint returns `null`. ## DELETE `/telegram/link/{link_id}` Remove a Telegram link. Response: ```json { "success": true } ``` ## POST `/telegram/test-message` Send a test notification to the current user's linked Telegram chat. Response: ```json { "success": true } ``` ## Internal Telegram Bot Endpoints These endpoints are intended for the bot process and require `X-Telegram-Bot-Secret`. ## POST `/telegram/bot/link/complete` Complete Telegram linking by consuming a previously issued link code. Request: ```json { "link_code": "ABC123DEF456", "telegram_user_id": "123456789", "chat_id": "123456789" } ``` Response: ```json { "id": 1, "user_id": 1, "telegram_user_id": "123456789", "chat_id": "123456789", "linked_at": "2026-06-07T00:05:00Z", "created_at": "2026-06-07T00:00:00Z" } ``` ## POST `/telegram/bot/status` Return server health data for the linked Telegram user. Request: ```json { "telegram_user_id": "123456789" } ``` Response: ```json { "username": "admin", "role": "admin", "servers": [ { "name": "prod-app-1", "environment": "production", "host": "10.0.0.10", "status": "ok", "reachable": true, "checked_at": "2026-06-07T00:00:00Z", "detail": null } ] } ``` ## POST `/telegram/bot/services` Return managed services visible to the linked Telegram user. Request: ```json { "telegram_user_id": "123456789", "server_name": "prod-app-1" } ``` Response: ```json { "username": "admin", "role": "admin", "services": [ { "server_name": "prod-app-1", "service_name": "nginx", "service_type": "systemd", "status": "running", "detail": null } ] } ``` ## POST `/telegram/bot/restart` Queue a restart action for a linked Telegram user with sufficient permissions. Request: ```json { "telegram_user_id": "123456789", "server_name": "prod-app-1", "service_name": "nginx" } ``` Response: ```json { "id": 103, "status": "pending" } ``` ## POST `/telegram/webhook` Webhook endpoint if webhook mode is used. Response: ```json { "success": true } ``` ## Common Error Examples ## 401 Unauthorized ```json { "code": "unauthorized", "message": "Authentication required", "request_id": "5b2d4a5d3b8745028b89f927c3ef98f8" } ``` ## 403 Forbidden ```json { "code": "forbidden", "message": "Insufficient permissions", "request_id": "5b2d4a5d3b8745028b89f927c3ef98f8" } ``` ## 404 Not Found ```json { "code": "not_found", "message": "Requested resource was not found", "request_id": "5b2d4a5d3b8745028b89f927c3ef98f8" } ``` ## 409 Conflict ```json { "code": "action_in_progress", "message": "A similar action is already running", "request_id": "5b2d4a5d3b8745028b89f927c3ef98f8" } ``` ## 422 Validation Error ```json { "code": "validation_error", "message": "Invalid request payload", "request_id": "5b2d4a5d3b8745028b89f927c3ef98f8" } ``` ## Minimal MVP Endpoint Set If you want to keep the first implementation extremely small, the minimum useful set is: - `POST /auth/login` - `GET /auth/me` - `GET /servers` - `POST /servers` - `GET /servers/{server_id}/health` - `GET /servers/{server_id}/metrics` - `GET /servers/{server_id}/services` - `POST /servers/{server_id}/services/{service_name}/restart` - `GET /servers/{server_id}/logs/{service_name}` - `GET /actions/{action_id}` - `GET /audit` - `POST /telegram/test-message`