feat: add initial implementation of the frontend panel with app management features

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Jakub Dorfman 2026-04-26 23:53:17 +02:00
parent 8cb90be7cd
commit e15298fd3d
5 changed files with 1547 additions and 557 deletions

147
API.md
View file

@ -1,29 +1,48 @@
# panel-api
A tiny local HTTP API wrapper around panelctl for future frontend integration.
HTTP API wrapper around panelctl with a web UI.
The service now also serves a lightweight web UI at `/`.
Default bind:
- 127.0.0.1:9911
Default bind: `127.0.0.1:9911`
## Endpoints
- GET /health
- GET /
- GET /apps
- GET /apps/<name>
- GET /apps/<name>/compose
- POST /apps/init
- POST /apps/<name>/compose
- POST /apps/<name>/render-route
- POST /apps/<name>/deploy
- POST /apps/<name>/stop
- POST /apps/<name>/remove
### Health & UI
| Method | Path | Description |
|--------|------|-------------|
| GET | `/` | Web UI (served from `frontend/index.html`) |
| GET | `/health` | Health check |
### Apps — Read
| Method | Path | Description |
|--------|------|-------------|
| GET | `/apps` | List all apps |
| GET | `/apps/<name>` | Show single app manifest |
| GET | `/apps/<name>/status` | Container status (running/stopped) |
| GET | `/apps/<name>/compose` | Read compose.yaml content |
| GET | `/apps/<name>/logs?tail=N` | Fetch last N log lines (default 100) |
| GET | `/apps/<name>/backups` | List available backups |
| GET | `/apps/<name>/backups/<file>` | Download backup zip |
### Apps — Write
| Method | Path | Description |
|--------|------|-------------|
| POST | `/apps/init` | Create a new app |
| POST | `/apps/<name>/deploy` | Deploy (compose up + caddy reload) |
| POST | `/apps/<name>/restart` | Restart (compose down + up) |
| POST | `/apps/<name>/stop` | Stop (compose down) |
| POST | `/apps/<name>/render-route` | Re-render Caddy route |
| POST | `/apps/<name>/compose` | Save compose.yaml content |
| POST | `/apps/<name>/validate-compose` | Validate compose file |
| POST | `/apps/<name>/backup` | Create volume backup (zip) |
| POST | `/apps/<name>/restore` | Restore from backup |
| POST | `/apps/<name>/remove` | Remove app |
## Example payloads
Create app:
### Create app (single domain)
```json
{
@ -34,7 +53,50 @@ Create app:
}
```
Remove and keep volumes:
### Create app (multiple domains)
```json
{
"name": "myapp",
"domain": "app.srazka.com,www.app.srazka.com",
"port": 18081,
"auth": true
}
```
Or using the `domains` array format:
```json
{
"name": "myapp",
"domains": ["app.srazka.com", "www.app.srazka.com"],
"port": 18081,
"auth": true
}
```
### Create app (wildcard domain)
```json
{
"name": "wildcard",
"domain": "*.srazka.com",
"port": 18082,
"auth": false
}
```
Note: Wildcard domains require DNS challenge configuration in Caddy.
### Save compose
```json
{
"content": "services:\n app:\n image: nginx:latest\n ports:\n - '127.0.0.1:18080:80'\n"
}
```
### Remove and keep volumes
```json
{
@ -42,9 +104,58 @@ Remove and keep volumes:
}
```
### Restore from backup
```json
{
"file": "whoami-20260101-120000.zip"
}
```
## Response format
All JSON responses include an `ok` boolean:
```json
{
"ok": true,
"apps": [...]
}
```
Error responses:
```json
{
"ok": false,
"error": "description",
"stderr": "panelctl error output"
}
```
## Status response
```json
{
"ok": true,
"name": "whoami",
"running": true,
"containers": [
{
"name": "whoami-app-1",
"state": "running",
"image": "docker.io/traefik/whoami:latest"
}
]
}
```
## Local test
```bash
curl -s http://127.0.0.1:9911/health | jq .
curl -s http://127.0.0.1:9911/apps | jq .
curl -s http://127.0.0.1:9911/apps/whoami/status | jq .
curl -s http://127.0.0.1:9911/apps/whoami/logs?tail=50 | jq .
curl -s http://127.0.0.1:9911/apps/whoami/backups | jq .
```