Das dokumentierte Backup lief real nie (stock postgres-Image ohne Cron, backup.sh nirgends eingebunden) -> bei Volume-/Disk-Verlust drohte Totalverlust. - Neuer service "backup" (docker-compose.yml): postgres:16-alpine als Sidecar, taeglicher pg_dump um BACKUP_HOUR (Default 03:00 Europe/Berlin) - backup-loop.sh: abhaengigkeitsfreier Tages-Scheduler (umgeht busybox-cron- Environment-Eigenheiten) - backup.sh korrigiert: laeuft remote gegen PGHOST mit PGPASSWORD; -Fc statt "-Fc | gzip" (war doppelt komprimiert und als .sql.gz nicht restore-bar) -> .dump - BACKUP_HOUR/BACKUP_RETENTION_DAYS konfigurierbar (.env.example) - operations.md: Backup-/Restore-Anleitung an Ist-Zustand angepasst Verifiziert: pg_dump erstellt + pg_restore in frische DB, Daten intakt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
103 lines
3.4 KiB
Markdown
103 lines
3.4 KiB
Markdown
# Operations Guide — ToolCTRL everythingbutwork
|
|
|
|
## Lokale Entwicklung
|
|
|
|
```bash
|
|
# Postgres (Docker)
|
|
docker compose -f docker-compose.dev.yml up -d postgres
|
|
|
|
# Migration anwenden
|
|
cp .env.example .env
|
|
pnpm --filter @cockpit/db migrate:up
|
|
|
|
# API und Web
|
|
pnpm --filter api dev # :3000
|
|
pnpm --filter web dev # :5173 (proxy /api+/trpc → :3000)
|
|
```
|
|
|
|
## Authentik-Setup (Story 1.3)
|
|
|
|
1. In Authentik (auth.tool-ctrl.de) als Admin einloggen.
|
|
2. **Providers → Create → OAuth2/OpenID Provider**
|
|
- Name: `cockpit`
|
|
- Authorization flow: `default-provider-authorization-explicit-consent`
|
|
- Client type: `Confidential`
|
|
- Client ID + Secret automatisch generieren lassen, in `.env` übertragen.
|
|
- Signing key: `authentik Self-signed Certificate`
|
|
- Redirect URIs: `https://cockpit.tool-ctrl.de/api/auth/callback` (Prod), `http://localhost:3000/api/auth/callback` (Dev)
|
|
3. **Applications → Create**
|
|
- Name: `everythingbutwork`
|
|
- Slug: `cockpit`
|
|
- Provider: `cockpit`
|
|
- Launch URL: `https://cockpit.tool-ctrl.de/`
|
|
4. `.env`-Variablen ausfüllen:
|
|
```
|
|
AUTHENTIK_ISSUER=https://auth.tool-ctrl.de/application/o/cockpit/
|
|
AUTHENTIK_CLIENT_ID=<aus Authentik>
|
|
AUTHENTIK_CLIENT_SECRET=<aus Authentik>
|
|
```
|
|
5. Gruppen-Mapping (OQ-1) wird im API-Code per `AUTHENTIK_GROUP_TO_AREA`-Variable umgesetzt (Erweiterung in späterer Story).
|
|
|
|
## Production-Deployment
|
|
|
|
```bash
|
|
# Auf der ToolCTRL-Infra:
|
|
git pull
|
|
docker compose pull
|
|
docker compose build
|
|
docker compose up -d
|
|
|
|
# Migration:
|
|
docker compose exec api node -e "import('@cockpit/db/client').then(async ({ createDbClient }) => { /* migrate via drizzle-kit in CI */ })"
|
|
# Alternativ direkt via drizzle-kit von außen mit Production-DATABASE_URL.
|
|
```
|
|
|
|
## Backups (Story 6.3)
|
|
|
|
Tägliches `pg_dump` läuft im **Backup-Sidecar** (`docker-compose.yml` service `backup`):
|
|
`scripts/backup-loop.sh` triggert `scripts/backup.sh` einmal pro Tag um
|
|
`BACKUP_HOUR` (Default 03:00 Europe/Berlin). Dump im **Custom-Format** (`-Fc`,
|
|
bereits komprimiert) nach `cockpit_backups:/backups` als `cockpit-<ts>.dump`.
|
|
Retention `BACKUP_RETENTION_DAYS` (Default 14). Nach jedem Lauf ein Heartbeat an
|
|
`POST /internal/cron/pg-dump-backup` (nur Logging — der Endpoint macht **kein**
|
|
Backup). Restore-Drill quartalsweise.
|
|
|
|
Konfiguration (optional, in `.env.production`): `BACKUP_HOUR`, `BACKUP_RETENTION_DAYS`.
|
|
|
|
```bash
|
|
# Status / letztes Backup ansehen
|
|
docker compose logs --tail=20 backup
|
|
docker compose exec backup ls -lh /backups
|
|
|
|
# Sofort ein Backup auslösen (außer der Reihe)
|
|
docker compose exec backup /usr/local/bin/backup.sh
|
|
```
|
|
|
|
### Restore-Anleitung
|
|
|
|
1. API stoppen: `docker compose stop api`
|
|
2. Backup aus `/backups` auswählen (`docker compose exec postgres ls -lh /backups`).
|
|
3. Restore in temporäre DB:
|
|
```bash
|
|
docker compose exec postgres createdb -U cockpit cockpit_restore
|
|
docker compose exec postgres pg_restore -U cockpit -d cockpit_restore /backups/<file>.dump
|
|
```
|
|
(In die Produktiv-DB stattdessen mit `--clean --if-exists -d cockpit`.)
|
|
4. Smoke-Test: `curl http://localhost:3000/api/health`
|
|
5. API wieder starten: `docker compose start api`
|
|
|
|
## Healthcheck
|
|
|
|
`GET /api/health` liefert:
|
|
|
|
```json
|
|
{ "status": "ok|degraded", "db": "ok|down", "authentik": "ok|down", "timestamp": "..." }
|
|
```
|
|
|
|
Wird von Docker und (Phase 2) externer Uptime-Überwachung genutzt.
|
|
|
|
## OQ-5 (Archivierung)
|
|
|
|
Soft-Delete via `project.archiviert=true` + `archiviert_am`. Kein automatisches Löschen.
|
|
Manuelle Bereinigung bei Bedarf durch DB-Admin via Migration.
|