180 lines
6.1 KiB
Markdown
180 lines
6.1 KiB
Markdown
# Feature: Backup
|
||
|
||
Ka-Note hat zwei unabhängige Backup-Systeme:
|
||
1. **Scheduled Backup** — automatische serverseitige ZIP-Exporte
|
||
2. **AI Workspace Export** — lock-basierter Export/Import für KI-Agenten
|
||
|
||
---
|
||
|
||
## 1. Scheduled Backup
|
||
|
||
### Konfiguration (Umgebungsvariablen)
|
||
|
||
| Variable | Default | Beschreibung |
|
||
|---|---|---|
|
||
| `BACKUP_ENABLED` | `false` | Feature aktivieren |
|
||
| `BACKUP_DIR` | `/data/backups` | Speicherort |
|
||
| `BACKUP_RETENTION_COUNT` | `14` | Letzte N Backups behalten |
|
||
| `BACKUP_CRON_HOUR` | `3` | UTC-Stunde für automatischen Lauf |
|
||
|
||
### Ablauf
|
||
|
||
- Server startet Scheduler beim Hochfahren (`msUntilNextRun()`)
|
||
- Täglich zur konfigurierten UTC-Stunde: ZIP pro User → `backup-{userId}-{timestamp}.zip`
|
||
- Alte Backups werden automatisch gelöscht (Retention Count)
|
||
|
||
### API Endpoints
|
||
|
||
| Method | Path | Beschreibung |
|
||
|---|---|---|
|
||
| `GET` | `/api/backup/status` | Konfiguration & Schedule-Status |
|
||
| `GET` | `/api/backup/export` | On-demand Export (kein Lock) |
|
||
| `POST` | `/api/backup/run` | Geplanten Backup manuell auslösen |
|
||
| `GET` | `/api/backup/list` | Gespeicherte Backups auflisten |
|
||
| `GET` | `/api/backup/download/:filename` | Backup herunterladen |
|
||
| `DELETE` | `/api/backup/:filename` | Backup löschen |
|
||
| `PATCH` | `/api/backup/config` | Retention Count ändern |
|
||
|
||
### Client UI (`/backup`)
|
||
|
||
- "Jetzt herunterladen" → `GET /api/backup/export`
|
||
- Auto-Backup-Statusanzeige (nur wenn `BACKUP_ENABLED=true`): nächster Lauf, letzter Lauf, Retention-Editor
|
||
- "Jetzt ausführen" → `POST /api/backup/run`
|
||
- Tabelle gespeicherter Backups (neueste zuerst) mit Download/Löschen
|
||
|
||
### Relevante Dateien
|
||
|
||
- `ka-note/server/src/lib/backup-service.ts`
|
||
- `ka-note/server/src/routes/backup.ts`
|
||
- `ka-note/client/src/routes/backup/+page.svelte`
|
||
|
||
---
|
||
|
||
## 2. AI Workspace Export
|
||
|
||
Lock-basierter Export/Import für KI-Agenten. Verhindert Sync-Konflikte während KI-Edits.
|
||
|
||
### Workflow
|
||
|
||
```
|
||
download.ps1 → [KI bearbeitet work/] → upload.ps1
|
||
| |
|
||
Lock acquired Lock released
|
||
```
|
||
|
||
Bei Unterbrechung: `unlock.ps1` gibt Lock frei ohne Upload.
|
||
|
||
### Lock-Mechanismus
|
||
|
||
- Lock-Ablauf: `AI_LOCK_EXPIRY_HOURS` (Standard 24h, Projekt: `168` = 7 Tage)
|
||
- Solange Lock aktiv: normaler Sync pausiert
|
||
- Token im `manifest.json` des ZIPs → wird beim Upload validiert
|
||
|
||
### API Endpoints
|
||
|
||
| Method | Path | Beschreibung |
|
||
|---|---|---|
|
||
| `GET` | `/api/ai/status` | Lock-Status prüfen |
|
||
| `GET` | `/api/ai/download` | Export + Lock acquiren |
|
||
| `POST` | `/api/ai/upload` | Import + Lock freigeben |
|
||
| `POST` | `/api/ai/unlock` | Lock ohne Upload freigeben |
|
||
|
||
Upload-Parameter: `?force=true` überschreibt Version-Konflikte.
|
||
|
||
### ZIP-Struktur
|
||
|
||
```
|
||
manifest.json # userId, exportedAt, lockToken, expiresAt, exportVersion
|
||
contexts.json # alle Contexts (inkl. soft-deleted)
|
||
topics.json # alle Topics
|
||
ratings.json # alle Ratings
|
||
history/{id}.meta.json # History-Entry-Metadaten
|
||
history/{id}.md # History-Entry-Text
|
||
wiki/{id}.meta.json # Wiki-Seiten-Metadaten
|
||
wiki/{id}.md # Wiki-Seiten-Text
|
||
images/{id}.{ext} # Binäre Bildblobs
|
||
notebooks.json
|
||
page_notebooks.json
|
||
```
|
||
|
||
**manifest.json-Felder:** `userId, exportedAt, lockToken, expiresAt, exportVersion`
|
||
|
||
### Pflichtfelder pro Entity
|
||
|
||
**Context:** `id, name, type, sortOrder, meta, archivedAt, isFavorite, updatedAt, deletedAt, purgedAt, version`
|
||
|
||
**Topic:** `id, contextId, title, status, snoozeUntil, sortOrder, isNew, updatedAt, deletedAt, purgedAt, version`
|
||
|
||
**History (meta):** `id, topicId, date (YYYY-MM-DD), sortOrder, linkedContextId, doneAt, wiedervorlageDate, wiedervorlageResolvedAt, updatedAt, deletedAt, purgedAt, version`
|
||
→ Kein `contextId`, kein `title` — History ist topic-scoped!
|
||
|
||
**Rating:** `id, topicId, historyEntryId, personName, value (1–4), comment, updatedAt, deletedAt, purgedAt, version`
|
||
|
||
### Versions-Konfliktauflösung
|
||
|
||
| Bedingung | Aktion |
|
||
|---|---|
|
||
| Client-Version > Server-Version | Update |
|
||
| Client-Version = Server-Version | Skip (keine Änderung) |
|
||
| Client-Version < Server-Version | **Conflict** |
|
||
| Conflicts ohne `force=true` | 409-Response, keine Änderungen |
|
||
| Mit `-Force` (PS-Script) | Retry mit `?force=true` |
|
||
|
||
### PowerShell Scripts (`ka-note/scripts/`)
|
||
|
||
| Script | Beschreibung |
|
||
|---|---|
|
||
| `download.ps1` | `work/` leeren, ZIP holen, entpacken, Lock-Info anzeigen |
|
||
| `upload.ps1` | ZIP mit .NET ZipArchive erstellen (Forward-Slash-Fix!), hochladen |
|
||
| `unlock.ps1` | Lock ohne Upload freigeben |
|
||
| `show-bundle.ps1` | Bundle-Inhalt (Lock-Token, Ablauf, Counts) anzeigen |
|
||
| `get-token.ps1` | Azure AD Bearer-Token holen/cachen |
|
||
| `set-token.ps1` | Token aus Clipboard/Argument speichern |
|
||
|
||
**Wichtig:** `download.ps1` löscht `work/` beim Start — Token nie dort ablegen!
|
||
|
||
**Windows-Pfad-Fix:** `upload.ps1` verwendet .NET `ZipArchive` statt `Compress-Archive`,
|
||
da `Compress-Archive` Backslash-Pfade erzeugt, der Server aber Forward-Slashes erwartet.
|
||
|
||
### Token-Workflow
|
||
|
||
```powershell
|
||
# Token aus Browser kopieren, dann:
|
||
.\set-token.ps1 # liest Clipboard → speichert nach ~\.ka-note\token.txt
|
||
.\get-token.ps1 # prüft JWT-Ablauf, gibt Token zurück
|
||
```
|
||
|
||
### Import-Helpers (`import-helpers.ps1`)
|
||
|
||
Dot-sourcen für eigene Import-Scripte:
|
||
|
||
```powershell
|
||
. .\import-helpers.ps1
|
||
Upsert-Context -Id "..." -Name "..." -Type "meeting"
|
||
Add-Topic -ContextId "..." -Title "..."
|
||
Add-HistoryEntry -TopicId "..." -Date "2026-02-24" -Body "..."
|
||
```
|
||
|
||
Vorhandene Import-Scripte: `import-jf-sysadmins.ps1`, `import-jf-landata.ps1`
|
||
|
||
### Relevante Dateien
|
||
|
||
- `ka-note/server/src/lib/ai-export-service.ts`
|
||
- `ka-note/server/src/lib/ai-agent-readme.ts`
|
||
- `ka-note/server/src/routes/ai-export.ts`
|
||
- `ka-note/client/src/lib/stores/aiLock.ts`
|
||
- `ka-note/scripts/download.ps1`
|
||
- `ka-note/scripts/upload.ps1`
|
||
- `ka-note/scripts/unlock.ps1`
|
||
|
||
---
|
||
|
||
## Gemeinsames ZIP-Format
|
||
|
||
Beide Systeme erzeugen strukturell identische ZIPs, unterscheiden sich nur im `manifest.json`:
|
||
- Scheduled Backup: `type: "backup"`
|
||
- AI Export: enthält `lockToken` + `expiresAt`
|
||
|
||
Soft-deleted Entities werden **immer exportiert** (mit gesetztem `deletedAt`).
|
||
Hard-delete existiert nicht.
|