# 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.