13 KiB
API
Purpose
This document defines the MVP REST API for the server panel backend.
Conventions
- Base path:
/api/v1 - Authentication:
Bearertoken - Response format: JSON
- Time format: ISO 8601 UTC
- Errors should return a stable
codeand human-readablemessage - Error responses also include
request_idfor log correlation
Authentication
POST /auth/login
Authenticate a user and return access and refresh tokens.
Request:
{
"username": "admin",
"password": "strong-password"
}
Response:
{
"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:
{
"refresh_token": "jwt-refresh-token"
}
Response:
{
"access_token": "new-access-token",
"token_type": "bearer"
}
POST /auth/logout
Invalidate the current session by bumping the user's token version.
Response:
{
"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:
{
"id": 1,
"username": "admin",
"role": "admin",
"is_active": true
}
Users
GET /users
Return user list. Admin only.
Response:
[
{
"id": 1,
"username": "admin",
"role": "admin",
"is_active": true
}
]
POST /users
Create a user. Admin only.
Request:
{
"username": "operator1",
"password": "strong-password",
"role": "operator"
}
PATCH /users/{user_id}
Update a user role or active flag. Admin only.
Request:
{
"role": "viewer",
"is_active": true
}
Servers
GET /servers
Return all configured servers visible to the current user.
Response:
[
{
"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:
{
"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:
{
"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:
{
"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:
{
"success": true
}
GET /servers/{server_id}/health
Return basic reachability and health information.
Response:
{
"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
sshservers only; - requires
ssh_usernameto be configured on the server record.
Response:
{
"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:
{
"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
unknownwith a detail message.
Response:
[
{
"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:
{
"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:
{
"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:
{
"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:
{
"id": 102,
"status": "pending"
}
POST /servers/{server_id}/services/{service_name}/restart
Queue a service restart action.
Response:
{
"id": 103,
"status": "pending"
}
Logs
GET /servers/{server_id}/logs
Return available log sources for the server.
Response:
{
"server_id": 1,
"sources": [
"nginx",
"app"
]
}
GET /servers/{server_id}/logs/{service_name}
Return recent log lines.
Query params:
lines: integer, optional, default100, max500
Notes:
- currently implemented for
sshservers only; - if
log_sourceis set, it must use the formatfile:/absolute/path; - otherwise logs come from
journalctlforsystemdservices ordocker logsfor Docker services.
Response:
{
"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:
{
"server_id": 1,
"target_type": "service",
"target_name": "nginx",
"action": "restart"
}
Response:
{
"action_id": 104,
"status": "pending"
}
GET /actions
Return recent actions with optional filters.
Suggested query params:
server_idstatusrequested_bylimit
Response:
[
{
"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:
{
"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_idtarget_typeactionlimit
Response:
[
{
"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:
{
"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:
{
"enabled": true,
"mode": "polling",
"linked_users_count": 2
}
POST /telegram/link
Issue a one-time Telegram link code for the authenticated user.
Response:
{
"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:
{
"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:
{
"success": true
}
POST /telegram/test-message
Send a test notification to the current user's linked Telegram chat.
Response:
{
"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:
{
"link_code": "ABC123DEF456",
"telegram_user_id": "123456789",
"chat_id": "123456789"
}
Response:
{
"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:
{
"telegram_user_id": "123456789"
}
Response:
{
"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:
{
"telegram_user_id": "123456789",
"server_name": "prod-app-1"
}
Response:
{
"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:
{
"telegram_user_id": "123456789",
"server_name": "prod-app-1",
"service_name": "nginx"
}
Response:
{
"id": 103,
"status": "pending"
}
POST /telegram/webhook
Webhook endpoint if webhook mode is used.
Response:
{
"success": true
}
Common Error Examples
401 Unauthorized
{
"code": "unauthorized",
"message": "Authentication required",
"request_id": "5b2d4a5d3b8745028b89f927c3ef98f8"
}
403 Forbidden
{
"code": "forbidden",
"message": "Insufficient permissions",
"request_id": "5b2d4a5d3b8745028b89f927c3ef98f8"
}
404 Not Found
{
"code": "not_found",
"message": "Requested resource was not found",
"request_id": "5b2d4a5d3b8745028b89f927c3ef98f8"
}
409 Conflict
{
"code": "action_in_progress",
"message": "A similar action is already running",
"request_id": "5b2d4a5d3b8745028b89f927c3ef98f8"
}
422 Validation Error
{
"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/loginGET /auth/meGET /serversPOST /serversGET /servers/{server_id}/healthGET /servers/{server_id}/metricsGET /servers/{server_id}/servicesPOST /servers/{server_id}/services/{service_name}/restartGET /servers/{server_id}/logs/{service_name}GET /actions/{action_id}GET /auditPOST /telegram/test-message