47ec7dc9a73c8302469e7ae14a2e92b67675d67a
Backup-Dashboard
Proof of concept backup dashboard, intended to display backup jobs from multiple sources.
collectors ──POST /api/v1/ingest (API key)──▶ ┌──────────────┐
│ FastAPI + │
browser ──nginx (basic auth, TLS)──▶ /api/* ─▶│ SQLite │
└─ static files: public/ └──────────────┘
public/: static frontend (no build step). Add?mock=1to the URL to use built-in sample data.backend/: FastAPI API backed by a single SQLite file.deploy/: nginx site and systemd unit.
Data model
- Tenant (company) → Job → Run.
- A job is keyed by
(tenant, source_system, job.external_id), so renaming a job at the source updates it instead of creating a duplicate. - A run has
status(running|success|warning|failed),started_atandfinished_at. The dashboard's Last Run is the most recentfinished_at. While a job is running it shows Running, and Last Run still shows when the previous run finished. - Runs older than
BACKUP_DASH_RETENTION_DAYS(default 90, minimum 30) are purged every 6 hours.
Reporting results (collectors)
Each collector (a script on a Veeam server, a webhook relay, etc.) gets its own API key:
cd backend && python manage.py create-key veeam-acme
Then it POSTs one report per run. Send running when a run starts (optional) and the final status
when it ends. Reports with the same started_at (or run.external_id) update the same run, and
retries are safe.
curl -X POST https://backups.example.com/api/v1/ingest \
-H "Authorization: Bearer bdk_..." -H "Content-Type: application/json" \
-d '{
"tenant": "Acme Corp",
"source_system": "Veeam B&R",
"job": { "external_id": "4f1c…", "name": "File Server – Nightly", "next_run_at": "2026-10-05T22:00:00-04:00" },
"run": {
"status": "success",
"started_at": "2026-10-04T22:00:05-04:00",
"finished_at": "2026-10-04T22:47:12-04:00",
"message": "Job completed successfully.",
"size_bytes": 128849018880,
"details": { "repository": "REPO-01" }
}
}'
- All timestamps must include a timezone offset (
Get-Date -Format oin PowerShell does this). job.next_run_atcontrols the Overdue flag. If you omit it, the stored value is kept. If you sendnull, it's cleared.runcan be omitted to only register or update a job.- Unknown fields are rejected (HTTP 422), so typos in collector scripts fail loudly.
API (read)
| Endpoint | Returns |
|---|---|
GET /api/v1/jobs |
All jobs with current status, running_since, last_run_at, next_run_at |
GET /api/v1/jobs/{id} |
One job, plus the last finished run's duration, size, message and details |
GET /api/v1/jobs/{id}/runs?days=30 |
Run history (no messages), newest first |
GET /api/v1/runs/{id} |
One run including message and details |
GET /api/v1/health |
{"status": "ok"} |
The OpenAPI schema is at /api/v1/openapi.json.
Authentication
- Dashboard users: nginx basic auth over HTTPS (
deploy/nginx.conf). Every user can see every tenant. Planned: Microsoft 365 sign-in through oauth2-proxy with the Entra ID provider and nginxauth_request. That's an nginx-only change. The frontend already tells the user to reload when a session expires (HTTP 401). - Collectors: per-collector bearer keys, checked by the app. Only a SHA-256 hash is stored.
To revoke one, run
python manage.py revoke-key NAME.
Development
cd backend
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
pytest
python manage.py seed-demo # 30 days of fake history
BACKUP_DASH_SERVE_STATIC=1 uvicorn --factory app.main:create_app --reload
# → http://localhost:8000
Deployment (Linux, nginx)
- Copy the repo to
/opt/backup-dashboard, create a venv at/opt/backup-dashboard/venv, and runpip install -r backend/requirements.txt. - Create a
backupdashsystem user, then installdeploy/backup-dashboard-api.serviceinto/etc/systemd/system/and enable it. - Copy
public/to/var/www/backup-dashboard/public, installdeploy/nginx.conf, get a certificate with certbot, and create users withhtpasswd -B. - Run the
manage.pycommands as the service user withBACKUP_DASH_DB=/var/lib/backup-dashboard/backup-dashboard.db.
Back up the SQLite file with sqlite3 backup-dashboard.db ".backup copy.db". Don't use a plain cp
while the service is running.
Description
Proof of concept backup dashboard, intended to display backup jobs from multiple sources
395 KiB
Languages
PowerShell
52.5%
Python
19.1%
JavaScript
18.1%
CSS
5.9%
HTML
4.4%