commit ea628b9a0a915e9b6ac7e22e19a4c7a66e5f6cab Author: Andrej Spielmann Date: Tue Apr 21 10:53:28 2026 +0200 Initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..076df71 Binary files /dev/null and b/.DS_Store differ diff --git a/API/API Keys.md b/API/API Keys.md new file mode 100644 index 0000000..696edaa --- /dev/null +++ b/API/API Keys.md @@ -0,0 +1,3 @@ +Ollama Hermes_Websearch: 6875e5e8701c414b845a82fdbbf3f628.P5LAYphcXIHqd5MLICDu1Q1S + +Gitea Obsidian_MAC: 656323c4f143a33241a20c993c5832e9ba81608d \ No newline at end of file diff --git a/Notes/.DS_Store b/Notes/.DS_Store new file mode 100644 index 0000000..fc82959 Binary files /dev/null and b/Notes/.DS_Store differ diff --git a/Notes/.obsidian/app.json b/Notes/.obsidian/app.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Notes/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Notes/.obsidian/appearance.json b/Notes/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Notes/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Notes/.obsidian/community-plugins.json b/Notes/.obsidian/community-plugins.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/Notes/.obsidian/community-plugins.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/Notes/.obsidian/core-plugins.json b/Notes/.obsidian/core-plugins.json new file mode 100644 index 0000000..639b90d --- /dev/null +++ b/Notes/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} \ No newline at end of file diff --git a/Notes/.obsidian/graph.json b/Notes/.obsidian/graph.json new file mode 100644 index 0000000..7db60ea --- /dev/null +++ b/Notes/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 0.9874372290055001, + "close": true +} \ No newline at end of file diff --git a/Notes/.obsidian/workspace-mobile.json b/Notes/.obsidian/workspace-mobile.json new file mode 100644 index 0000000..f122ac1 --- /dev/null +++ b/Notes/.obsidian/workspace-mobile.json @@ -0,0 +1,176 @@ +{ + "main": { + "id": "62cf0038ff114e49", + "type": "split", + "children": [ + { + "id": "79e10c965f93ac09", + "type": "tabs", + "children": [ + { + "id": "437e9deae12a88ca", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "PaperlessNGX/PaperlessNGX_Log.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "PaperlessNGX_Log" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ca71fb4d750506af", + "type": "mobile-drawer", + "children": [ + { + "id": "edb7c0914f082eda", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Dateiexplorer" + } + }, + { + "id": "1360d4663c2db1d0", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Suchen" + } + }, + { + "id": "f5b2a77629417ecd", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "20f16c8dc8f03e5e", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "Alle Eigenschaften" + } + }, + { + "id": "0f0998d8770b6d35", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Lesezeichen" + } + } + ], + "currentTab": 0 + }, + "right": { + "id": "285d83fd90346800", + "type": "mobile-drawer", + "children": [ + { + "id": "d96384a36ee9e67d", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "SQLHosting/Anmeldedaten.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Rückverweise" + } + }, + { + "id": "3dfa9f6ad917f680", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "Entwicklung/SQLHostingDesk/Projektbeschreibung.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Ausgehende Links" + } + }, + { + "id": "e0575d30f1ece371", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "Entwicklung/SQLHostingDesk/Projektbeschreibung.md", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Gliederung" + } + } + ], + "currentTab": 0 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Schnellauswahl öffnen": false, + "graph:Graph-Ansicht öffnen": false, + "canvas:Neuen Canvas erstellen": false, + "daily-notes:Heutige Notiz öffnen": false, + "templates:Vorlage einfügen": false, + "command-palette:Befehlspalette öffnen": false, + "bases:Neue Base erstellen": false + } + }, + "active": "437e9deae12a88ca", + "lastOpenFiles": [ + "SQLHosting/Anmeldedaten.md", + "Entwicklung/SQLHostingDesk/Projektbeschreibung.md" + ] +} \ No newline at end of file diff --git a/Notes/.obsidian/workspace.json b/Notes/.obsidian/workspace.json new file mode 100644 index 0000000..a19c492 --- /dev/null +++ b/Notes/.obsidian/workspace.json @@ -0,0 +1,211 @@ +{ + "main": { + "id": "8c62124cec9c5041", + "type": "split", + "children": [ + { + "id": "b085adecc4e71c8c", + "type": "tabs", + "children": [ + { + "id": "2b7532e8a6084d2c", + "type": "leaf", + "state": { + "type": "bases", + "state": { + "file": "Unbenannt.base", + "viewName": "Tabelle" + }, + "icon": "lucide-table", + "title": "Unbenannt" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "66d8d1f5b340307d", + "type": "split", + "children": [ + { + "id": "564855d7574cb5eb", + "type": "tabs", + "children": [ + { + "id": "de7245107632f08f", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Dateiexplorer" + } + }, + { + "id": "264963109e69afe5", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Suchen" + } + }, + { + "id": "3617eb6a65577779", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Lesezeichen" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "6058dd084c583192", + "type": "split", + "children": [ + { + "id": "9548ea3ddd25834c", + "type": "tabs", + "children": [ + { + "id": "1b74be11690924a4", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "Willkommen.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Rückverweise auf Willkommen" + } + }, + { + "id": "54f544090f057bc8", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "Willkommen.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Ausgehende Links von Willkommen öffnen" + } + }, + { + "id": "4a4052d0c9374ad1", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "c63dcd43d454d9f5", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "Alle Eigenschaften" + } + }, + { + "id": "26a3517baa0fb4db", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "Willkommen.md", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Gliederung von Willkommen" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Schnellauswahl öffnen": false, + "graph:Graph-Ansicht öffnen": false, + "canvas:Neuen Canvas erstellen": false, + "daily-notes:Heutige Notiz öffnen": false, + "templates:Vorlage einfügen": false, + "command-palette:Befehlspalette öffnen": false, + "bases:Neue Base erstellen": false + } + }, + "active": "2b7532e8a6084d2c", + "lastOpenFiles": [ + "Entwicklung/SQLHostingDesk/Projektbeschreibung.md", + "Unbenannt.base", + "Entwicklung/WrestleDesk/Frontend.md", + "Entwicklung/SQLHostingDesk", + "Unbenannt.md", + "Trainings/Kinder/2026-03-10.md", + "Trainings/Kinder/2026-03-03.md", + "Trainings/Erwachsene/2026-03-03.md", + "Unbenannt.canvas", + "Entwicklung/WrestleDesk", + "Entwicklung", + "00-Notizen-Präferenzen.md", + "Trainings/Kinder", + "Trainings/Erwachsene", + "Trainings", + "Trainings/Erwachsene/2026-03-03 Erwachsene Training.md", + "Trainings/Kinder/2026-03-03 Kindertraining.md", + "2026-03-02.md", + "Trainings/Training am Donnerstag.md", + "Trainings/2026-03-03 Erwachsene Training.md", + "Willkommen.md", + "Welcome.md", + "flag_fetch.md" + ] +} \ No newline at end of file diff --git a/Notes/.trash/.DS_Store b/Notes/.trash/.DS_Store new file mode 100644 index 0000000..ca71953 Binary files /dev/null and b/Notes/.trash/.DS_Store differ diff --git a/Notes/.trash/2026-03-03 Erwachsene Training.md b/Notes/.trash/2026-03-03 Erwachsene Training.md new file mode 100644 index 0000000..589450a --- /dev/null +++ b/Notes/.trash/2026-03-03 Erwachsene Training.md @@ -0,0 +1,13 @@ +## Gestriges Training (Erwachsene) – 2026-03-03 + +**Trainer:** Gabor (hat das Training übernommen) + +**Inhalt:** +- Coole Sachen mit Rumreißer gezeigt +- Als Verteidigungstechnik + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/2026-03-03 Kindertraining.md b/Notes/.trash/2026-03-03 Kindertraining.md new file mode 100644 index 0000000..ea48ed4 --- /dev/null +++ b/Notes/.trash/2026-03-03 Kindertraining.md @@ -0,0 +1,18 @@ +## Kindertraining – 2026-03-03 + +**Trainer:** Andrej (ich) + +**Inhalt:** +- +- + +**Besonderes:** +- Ein Mädchen (5 Jahre) hat sich an der Augenbraue verletzt, Wunde ist aufgegangen (ca. 3-4 cm). +- Sie war gestern neu mit ihren Geschwistern (insgesamt 5 Kinder). +- Die Familie hat Anmeldezettel abgegeben (muss noch abgegeben werden). + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/Kinder/2026-03-03.md b/Notes/.trash/Kinder/2026-03-03.md new file mode 100644 index 0000000..ea48ed4 --- /dev/null +++ b/Notes/.trash/Kinder/2026-03-03.md @@ -0,0 +1,18 @@ +## Kindertraining – 2026-03-03 + +**Trainer:** Andrej (ich) + +**Inhalt:** +- +- + +**Besonderes:** +- Ein Mädchen (5 Jahre) hat sich an der Augenbraue verletzt, Wunde ist aufgegangen (ca. 3-4 cm). +- Sie war gestern neu mit ihren Geschwistern (insgesamt 5 Kinder). +- Die Familie hat Anmeldezettel abgegeben (muss noch abgegeben werden). + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/Kinder/2026-03-10.md b/Notes/.trash/Kinder/2026-03-10.md new file mode 100644 index 0000000..51213dc --- /dev/null +++ b/Notes/.trash/Kinder/2026-03-10.md @@ -0,0 +1,16 @@ +# 2026-03-10 - Kindertraining + +**Datum:** Dienstag, 10. März 2026 +**Typ:** Kindertraining +**Ort:** + +## Notizen + +--- + +## Fokus / Themen + +--- + +## Anmerkungen + diff --git a/Notes/.trash/Trainings 2/Erwachsene/2026-03-03 Erwachsene Training.md b/Notes/.trash/Trainings 2/Erwachsene/2026-03-03 Erwachsene Training.md new file mode 100644 index 0000000..589450a --- /dev/null +++ b/Notes/.trash/Trainings 2/Erwachsene/2026-03-03 Erwachsene Training.md @@ -0,0 +1,13 @@ +## Gestriges Training (Erwachsene) – 2026-03-03 + +**Trainer:** Gabor (hat das Training übernommen) + +**Inhalt:** +- Coole Sachen mit Rumreißer gezeigt +- Als Verteidigungstechnik + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/Trainings 2/Erwachsene/2026-03-03.md b/Notes/.trash/Trainings 2/Erwachsene/2026-03-03.md new file mode 100644 index 0000000..589450a --- /dev/null +++ b/Notes/.trash/Trainings 2/Erwachsene/2026-03-03.md @@ -0,0 +1,13 @@ +## Gestriges Training (Erwachsene) – 2026-03-03 + +**Trainer:** Gabor (hat das Training übernommen) + +**Inhalt:** +- Coole Sachen mit Rumreißer gezeigt +- Als Verteidigungstechnik + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/Trainings/Erwachsene/2026-03-03 Erwachsene Training.md b/Notes/.trash/Trainings/Erwachsene/2026-03-03 Erwachsene Training.md new file mode 100644 index 0000000..589450a --- /dev/null +++ b/Notes/.trash/Trainings/Erwachsene/2026-03-03 Erwachsene Training.md @@ -0,0 +1,13 @@ +## Gestriges Training (Erwachsene) – 2026-03-03 + +**Trainer:** Gabor (hat das Training übernommen) + +**Inhalt:** +- Coole Sachen mit Rumreißer gezeigt +- Als Verteidigungstechnik + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/.trash/Trainings/Training am Donnerstag.md b/Notes/.trash/Trainings/Training am Donnerstag.md new file mode 100644 index 0000000..e69de29 diff --git a/Notes/AgentMail_Konfiguration.md b/Notes/AgentMail_Konfiguration.md new file mode 100644 index 0000000..ebf137e --- /dev/null +++ b/Notes/AgentMail_Konfiguration.md @@ -0,0 +1,47 @@ +# AgentMail Konfiguration + +**Aktualisiert:** 2026-04-20 + +## Postfach Details + +| Feld | Wert | +|------|------| +| **Postfach (Inbox)** | xopenclawgui@agentmail.to | +| **API Key** | am_us_268938e58208cc51020c84aa9e95cffe13c3c38b73fd6fbae220c6f4a075867e | +| **Status** | ✅ Aktiv | +| **Provider** | AgentMail (agentmail.to) | + +## Verwendung + +Dieses Postfach wird vom Hermes Agent genutzt um: +- E-Mails zu senden +- E-Mails zu empfangen +- Benachrichtigungen zu versenden +- Automatische Reports zu verschicken + +## Sicherheit + +⚠️ **WICHTIG:** Dieser API Key ist geheim und sollte nicht geteilt werden! + +## Integration + +Konfiguriert in: `~/.hermes/config.yaml` + +```yaml +mcp_servers: + agentmail: + command: npx + args: + - -y + - agentmail-mcp + env: + AGENTMAIL_API_KEY: am_us_268938e58208cc51020c84aa9e95cffe13c3c38b73fd6fbae220c6f4a075867e +``` + +## Verlauf + +- **2026-04-20:** API Key aktualisiert (alter Key war abgelaufen) + +--- + +*Diese Datei enthält sensible Informationen - sicher aufbewahren!* diff --git a/Notes/PaperlessNGX/API_Dokumentation_Context7.md b/Notes/PaperlessNGX/API_Dokumentation_Context7.md new file mode 100644 index 0000000..76ae02e --- /dev/null +++ b/Notes/PaperlessNGX/API_Dokumentation_Context7.md @@ -0,0 +1,181 @@ +# Paperless-ngx API Dokumentation (Context7) + +**Quelle:** Context7 MCP Server +**Abrufdatum:** 2026-04-20 +**Library ID:** `/paperless-ngx/paperless-ngx` + +--- + +## 🆓 Neue Features & Endpunkte + +### 1. Workflows API (NEU!) +Automatisierte Workflows mit Triggern und Actions: + +```bash +# Workflows auflisten +GET /api/workflows/ + +# Workflow erstellen +POST /api/workflows/ +Content-Type: application/json + +{ + "name": "Auto-tag Invoices", + "enabled": true, + "triggers": [{ + "type": 2, + "filter_filename": "*.pdf", + "matching_algorithm": 3, + "match": "invoice" + }], + "actions": [{ + "type": 1, + "assign_tags": [5], + "assign_document_type": 2 + }] +} +``` + +**Trigger-Typen:** +- Dokument-Upload +- Filename-Matching +- Content-Matching + +**Action-Typen:** +- Tags zuweisen +- Dokumententyp zuweisen +- Korrespondent zuweisen +- Benachrichtigungen senden + +--- + +### 2. Tasks API (NEU!) +Verfolgung von asynchronen Aufgaben: + +```bash +# Task-Status prüfen +GET /api/tasks/?task_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890 + +# Response: +{ + "task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "status": "SUCCESS", // PENDING, SUCCESS, FAILED + "related_document": 456 +} +``` + +--- + +### 3. Dokument-Upload mit Metadaten + +```bash +# Basis-Upload +POST /api/documents/post_document/ +-F "document=@/path/to/invoice.pdf" + +# Upload mit vollständigen Metadaten +POST /api/documents/post_document/ +-F "document=@/path/to/invoice.pdf" +-F "title=Company Invoice Q4 2024" +-F "correspondent=5" +-F "document_type=3" +-F "tags=1" +-F "tags=7" +-F "created=2024-10-15" +-F "archive_serial_number=00142" +-F "custom_fields=[{\"field\": 1, \"value\": \"Custom Value\"}]" + +# Response: Task UUID +"550e8400-e29b-41d4-a716-446655440000" +``` + +--- + +### 4. Dokument-Verwaltung + +```bash +# Dokument abrufen +GET /api/documents/{id}/ + +# Metadaten aktualisieren (PATCH!) +PATCH /api/documents/{id}/ +Content-Type: application/json + +{ + "title": "Updated Invoice Title", + "correspondent": 5, + "document_type": 2, + "tags": [1, 3, 7], + "archive_serial_number": 142, + "custom_fields": [{"field": 1, "value": "Updated Value"}] +} + +# Original herunterladen +GET /api/documents/{id}/download/ + +# Archiv-Version (PDF/A) herunterladen +GET /api/documents/{id}/download/?original=false +``` + +--- + +### 5. Custom Fields API + +```bash +# Custom Fields auflisten +GET /api/custom_fields/ + +# Custom Field erstellen +POST /api/custom_fields/ +{ + "name": "Rechnungsnummer", + "data_type": "string", + "extra_data": {"select_options": []} +} +``` + +--- + +## 🔧 Integration Patterns + +### Scanner-to-Folder-to-Paperless +1. Scanner speichert in Netzwerk-Ordner +2. Paperless-ngx Consumer überwacht Ordner +3. OCR und Indexierung automatisch +4. Workflow-Regeln wenden Tags/Korrespondenten an + +### Email-basierte Ingestion +1. Email-Account überwachen +2. Anhänge automatisch extrahieren +3. In Paperless-ngx importieren +4. Absender als Korrespondent speichern + +### ERP-System Integration +```bash +# Rechnung aus ERP hochladen +# → Paperless-ngx verarbeitet OCR +# → API gibt Dokument-ID zurück +# → ERP speichert Dokument-ID für Verknüpfung +``` + +--- + +## 📊 Best Practices + +1. **Asynchrone Verarbeitung:** Uploads return Task UUID → Status prüfen +2. **Workflows nutzen:** Automatische Kategorisierung statt manuelles Tagging +3. **Custom Fields:** Für projektspezifische Metadaten +4. **Archive Serial Number:** Für physische Ablage-Verknüpfung +5. **PATCH statt PUT:** Für partielle Updates + +--- + +## 🔗 Weitere Ressourcen + +- **Paperless-AI:** `/clusterzx/paperless-ai` - AI-powered Klassifizierung +- **Paperless-GPT:** `/icereed/paperless-gpt` - LLM-enhanced OCR +- **Paperless-Stack:** `/timothystewart6/paperless-stack` - Docker Compose Setup + +--- + +*Diese Dokumentation wurde über Context7 MCP generiert und sollte regelmäßig aktualisiert werden.* diff --git a/Notes/PaperlessNGX/Dokumentenanalyse_Umbenennung.md b/Notes/PaperlessNGX/Dokumentenanalyse_Umbenennung.md new file mode 100644 index 0000000..f805ebe --- /dev/null +++ b/Notes/PaperlessNGX/Dokumentenanalyse_Umbenennung.md @@ -0,0 +1,174 @@ +# Paperless-ngx Dokumenten-Analyse & Umbenennungsvorschläge + +**Erstellt am:** 2026-04-20 +**Anzahl Dokumente:** 70 +**Status:** ✅ Analyse abgeschlossen + +--- + +## 📊 Übersicht nach Kategorien + +| Kategorie | Anzahl | Status | +|-----------|--------|--------| +| 🏦 Bank | 26 | Vorschläge erstellt | +| 📄 Sonstiges | 28 | Überprüfung empfohlen | +| 🚗 Fahrzeug | 6 | Vorschläge erstellt | +| 💰 Rechnung | 3 | Vorschläge erstellt | +| 🛡️ Versicherung | 3 | Vorschläge erstellt | +| 🏛️ Behörde | 2 | Vorschläge erstellt | +| 📑 Steuer | 1 | Vorschläge erstellt | +| ✍️ Vertrag | 1 | Vorschläge erstellt | + +--- + +## 🏦 Bank (26 Dokumente) + +**Muster:** `YYYY-MM_Bank_Kontoauszug_[Kontoart]-[Kontonummer]` + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 61 | Girokonto_5429905347_Kontoauszug_20240104 | 2024-01_Bank_Kontoauszug_Girokonto-5429905347 | Bank, Kontoauszug | +| 58 | Girokonto_5429905347_Kontoauszug_20231202 | 2023-12_Bank_Kontoauszug_Girokonto-5429905347 | Bank, Kontoauszug | +| 55 | Girokonto_5429905347_Kontoauszug_20231102 | 2023-11_Bank_Kontoauszug_Girokonto-5429905347 | Bank, Kontoauszug | +| 57 | Girokonto_5429905347_Kontoauszug_20231002 | 2023-10_Bank_Kontoauszug_Girokonto-5429905347 | Bank, Kontoauszug | +| ... | ... | ... | ... | + +**Empfohlene Tags:** `Bank`, `Kontoauszug`, `Girokonto` + +--- + +## 🚗 Fahrzeug (6 Dokumente) + +### Suzuki Gladius SFV-650 + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 72 | Suzuki Gladius SFV-650 Kaufvertrag | 2026-04-06_Fahrzeug_Suzuki-Gladius_Kaufvertrag | Fahrzeug, Motorrad, Kaufvertrag, Suzuki | +| 71 | Suzuki Gladius SFV-650 Brief Groß | 2026-04-06_Fahrzeug_Suzuki-Gladius_Fahrzeugbrief-Teil2 | Fahrzeug, Motorrad, Brief, Suzuki | +| 70 | Suzuki Gladius SFV-650 Brief klein | 2026-04-06_Fahrzeug_Suzuki-Gladius_Fahrzeugschein-Teil1 | Fahrzeug, Motorrad, Schein, Suzuki | +| 68 | betriebsanleitung-a2-a-suzuki-sv650 | 2010-04-06_Fahrzeug_Suzuki-Gladius_Betriebsanleitung | Fahrzeug, Motorrad, Suzuki, Anleitung | + +### BMW 320D + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 66 | TÜV 320D Alireza | 2024-10-09_Fahrzeug_BMW-320D_TÜV-Hauptuntersuchung | Fahrzeug, Auto, TÜV, BMW | +| 64 | BMW_Kaufvertrag | 2024_Fahrzeug_BMW-320D_Kaufvertrag | Fahrzeug, Auto, Kaufvertrag, BMW | + +--- + +## 💰 Rechnung (3 Dokumente) + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 69 | Rechnung Fahrschule | 2026-04-08_Rechnung_Fahrschule-Gut-SO_Führerschein-Klasse-A | Rechnung, Fahrschule, Führerschein | +| 67 | Rechnung 24101031 | 2024_Rechnung_24101031 | Rechnung | +| 30 | Kleingartenverein Erlengrund Abrechnung 2024 | 2024_Rechnung_Kleingartenverein_Erlengrund_Jahresabrechnung | Rechnung, Kleingarten, Verein | + +--- + +## 🛡️ Versicherung (3 Dokumente) + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 10 | Die nächsten Schritte zur Ihrer Versicherung | 2023_Versicherung_Neue-Versicherung-Schritte | Versicherung | +| 25 | Meldebescheinigung zu Sozialen Versicherung 2023 | 2023_Versicherung_Meldebescheinigung_Soziale-Versicherung | Versicherung, Sozialversicherung | +| 7 | Vorlaeufige_Versicherungsbescheinigung | 2023_Versicherung_Vorlaeufige-Bescheinigung | Versicherung | + +--- + +## 🏛️ Behörde (2 Dokumente) + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 16 | Abgabebescheid 2023 | 2023_Behörde_Abgabebescheid | Behörde, Bescheid | +| 24 | Abgabebescheid 2024 | 2024_Behörde_Abgabebescheid | Behörde, Bescheid | + +--- + +## 📑 Steuer (1 Dokument) + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 14 | Lohnsteuerbescheinigung 2022 | 2022_Steuer_Lohnsteuerbescheinigung | Steuer, Lohnsteuer, 2022 | + +--- + +## ✍️ Vertrag (1 Dokument) + +| ID | Aktueller Titel | Vorgeschlagener Titel | Tags | +|----|-----------------|----------------------|------| +| 62 | Überlassungsvertrag | 2020_Vertrag_Überlassungsvertrag | Vertrag | + +--- + +## 📄 Sonstiges (28 Dokumente) - Überprüfung empfohlen + +Diese Dokumente haben nicht eindeutige Titel und sollten manuell kategorisiert werden: + +**Gehaltsabrechnungen (vermutlich):** +- ID 28: Gehalt Dezember 2023 +- ID 31: Gehalt Januar 2024 +- ID 27: Gehalt Oktober 2023 +- ID 26: Gehalt September 2023 + +**Empfohlene Umbenennung:** `2023-12_Gehalt_[Arbeitgeber]_Abrechnung` + +**Wohnungseigentümerversammlungen:** +- ID 17: Protkotoll der orderntlichen Eigentümerversammlung von 23.06.2022 +- ID 20: Protkotoll der orderntlichen Eigentümerversammlung von 21.07.2023 + +**Empfohlene Umbenennung:** `2023-07-21_Wohnung_Eigentümerversammlung-Protokoll` + +**Renteninformationen:** +- ID 23: Regina Spielmann - Renteninformation 2023 +- ID 18: RentenInformation 2023 - Andrej Spielmann + +**Empfohlene Umbenennung:** `2023_Rente_Renteninformation_[Name]` + +**Unkategorisiert (bitte prüfen):** +- ID 19: Bestimmung der Wirtschaftlichen Einheit - 03.05.2023 +- ID 6: 1und1 Service Pin +- ID 8: TK-Mitglied werden +- ID 9: Antrags-ID +- ID 22: Freischaltcode TK +- ID 15: Hort Pascal - Leistung +- ID 32: eVB-Schreiben +- ID 65: Gutachten 24101109 +- ID 29: Preisanpassung Betreuungskosten +- ID 63: QR Hand gebrochen +- ID 34: RAC_16955720_20240213 +- ID 33: download + +--- + +## 🎯 Empfohlene Tags zu erstellen + +1. **Fahrzeug** - Für alle Fahrzeugunterlagen +2. **Motorrad** - Spezifisch für Suzuki +3. **Auto** - Spezifisch für BMW +4. **Kaufvertrag** - Für Kaufverträge +5. **TÜV** - Für Hauptuntersuchungen +6. **Bank** - Für Kontoauszüge +7. **Rechnung** - Für alle Rechnungen +8. **Versicherung** - Für Versicherungsunterlagen +9. **Steuer** - Für Steuerbescheinigungen +10. **Behörde** - Für Behördliche Dokumente +11. **Vertrag** - Für Verträge aller Art +12. **Gehalt** - Für Gehaltsabrechnungen +13. **Rente** - Für Renteninformationen +14. **Wohnung** - Für WEG-Protokolle + +--- + +## 📝 Nächste Schritte + +1. [ ] Tags in Paperless-ngx erstellen +2. [ ] Dokumente nach Vorschlägen umbenennen +3. [ ] Tags zuweisen +4. [ ] Korrespondenten ergänzen (wo sinnvoll) +5. [ ] "Sonstiges" Dokumente manuell kategorisieren + +--- + +*Automatisch generiert durch Paperless-ngx Skill* diff --git a/Notes/PaperlessNGX/PaperlessNGX_Anweisungen.md b/Notes/PaperlessNGX/PaperlessNGX_Anweisungen.md new file mode 100644 index 0000000..0aa3034 --- /dev/null +++ b/Notes/PaperlessNGX/PaperlessNGX_Anweisungen.md @@ -0,0 +1,89 @@ +# Paperless-ngx Anweisungen + +## API Konfiguration +- **URL:** http://192.168.101.42:8000 +- **Benutzer:** PlayMan +- **API Token:** (Im Memory gespeichert) + +## Ordnerstruktur +- `PaperlessNGX_Log.md` - Alle API-Aktionen werden hier geloggt +- `Dokumente/` - Exportierte Dokumente (falls vorhanden) + +## Verfügbare Aktionen + +### Dokumente verwalten +1. **Liste Dokumente** - Alle Dokumente anzeigen +2. **Dokument hochladen** - PDF/Bilder importieren +3. **Dokument suchen** - Volltextsuche +4. **Dokument aktualisieren** - Tags, Titel, Korrespondent ändern +5. **Dokument löschen** - Aus Paperless entfernen + +### Tags & Korrespondenten +1. **Tags anzeigen** - Alle verfügbaren Tags +2. **Tag erstellen** - Neuen Tag hinzufügen +3. **Korrespondenten anzeigen** - Alle Absender +4. **Korrespondent erstellen** - Neuen Absender hinzufügen + +### System +1. **Status prüfen** - API-Status, Index, Speicher +2. **Tasks anzeigen** - Laufende Verarbeitungen + +## Workflow für Dokumenten-Umbenennung + +### 1. Neue Dokumente erkennen +- Suche nach Dokumenten OHNE "AI" Tag +- Diese sind neu oder noch nicht bearbeitet + +### 2. Analyse durchführen +- OCR-Text lesen +- Inhalt kategorisieren (Fahrzeug, Rechnung, Bank, etc.) +- Datum extrahieren +- Korrespondenten identifizieren + +### 3. Umbenennungs-Schema +**Format:** `JJJJ-MM-TT_Kategorie_Beschreibung_Zusatz` + +**Beispiele:** +- `2026-04-06_Fahrzeug_Suzuki-Gladius_Kaufvertrag` +- `2024-01_Bank_Kontoauszug_Girokonto-5429905347` +- `2026-04-08_Rechnung_Fahrschule-Gut-SO_Führerschein-Klasse-A` + +### 4. Tags zuweisen +**Immer hinzufügen:** +- `AI` - Markiert als bearbeitet durch KI +- Kategorie-Tag (z.B. `Fahrzeug`, `Rechnung`, `Bank`) +- Spezifische Tags (z.B. `Suzuki`, `Kaufvertrag`) + +### 5. In Obsidian loggen +- Jede Änderung in `PaperlessNGX_Log.md` dokumentieren +- Alte und neue Titel notieren +- Zugewiesene Tags vermerken + +### 6. Dokumente ohne "AI" Tag finden +```bash +# API-Call um unbearbeitete Dokumente zu finden +curl -H "Authorization: Token $TOKEN" \ + "http://192.168.101.42:8000/api/documents/?tags__name__none=AI" +``` + +## Wichtige Hinweise + +- **OCR:** Automatisch für PDFs und Bilder +- **Max Dateigröße:** 100MB pro Datei +- **Unterstützte Formate:** PDF, JPG, PNG, TIFF, GIF, BMP +- **Backup:** Regelmäßig über Paperless-ngx UI erstellen +- **AI-Tag:** Immer setzen um Bearbeitung zu markieren + +## Nächste Aufgaben + +- [ ] Teste API-Verbindung +- [ ] Erstelle erste Tags +- [ ] Lade Testdokument hoch +- [ ] Dokumentiere Workflow + +## Letzte Änderungen + +- 2026-04-20: 16 Dokumente umbenannt mit AI-Tag +- Tags erstellt: AI, Fahrzeug, Bank, Versicherung, Steuer, Behörde, Vertrag +- Workflow für automatische Dokumentenkennzeichnung etabliert +- 2026-04-20: Skill erstellt, API-Verbindung getestet diff --git a/Notes/PaperlessNGX/PaperlessNGX_Log.md b/Notes/PaperlessNGX/PaperlessNGX_Log.md new file mode 100644 index 0000000..761a374 --- /dev/null +++ b/Notes/PaperlessNGX/PaperlessNGX_Log.md @@ -0,0 +1,131 @@ +# Paperless-ngx Log + +## 2026-04-20 13:23:00 + +**Aktion:** Skill erstellt und API-Verbindung getestet +**Status:** ✅ Erfolgreich +**Details:** +- API-URL: http://192.168.101.42:8000 +- Paperless-ngx Version: 2.20.14 +- Installations-Typ: Docker auf Unraid +- Datenbank: PostgreSQL (Status: OK) +- Index-Status: OK +- Classifier-Status: OK + +**Nächste Schritte:** +- [ ] Dokumente auflisten +- [ ] Tags erstellen +- [ ] Testdokument hochladen + +## 2026-04-20 15:00:00 + +**Aktion:** Massenumbenennung durchgeführt +**Status:** ✅ Erfolgreich - 16 Dokumente umbenannt +**Details:** +- Tags erstellt: AI, Fahrzeug, Bank, Versicherung, Steuer, Behörde, Vertrag +- 16 Dokumente umbenannt und mit AI-Tag versehen +- Schema: `JJJJ-MM-TT_Kategorie_Beschreibung_Zusatz` + +**Umbenannte Dokumente:** +- Fahrzeug: 6 Dokumente (Suzuki + BMW) +- Rechnung: 3 Dokumente +- Versicherung: 3 Dokumente +- Steuer: 1 Dokument +- Behörde: 2 Dokumente +- Vertrag: 1 Dokument + +**Verbleibend:** 54 Dokumente (Bank + Sonstiges) für spätere Bearbeitung + +--- + +**Aktion:** Dokumentenanalyse durchgeführt +**Status:** ✅ Erfolgreich +**Details:** +- 70 Dokumente analysiert +- 8 Kategorien identifiziert +- Umbenennungsvorschläge erstellt +- Gespeichert in: `Dokumentenanalyse_Umbenennung.md` + +**Kategorien:** +- Bank: 26 Dokumente (Kontoauszüge) +- Sonstiges: 28 Dokumente (Überprüfung empfohlen) +- Fahrzeug: 6 Dokumente (Suzuki + BMW) +- Rechnung: 3 Dokumente +- Versicherung: 3 Dokumente +- Behörde: 2 Dokumente +- Steuer: 1 Dokument +- Vertrag: 1 Dokument + +**Nächste Schritte:** +- [ ] Tags in Paperless-ngx erstellen +- [ ] Dokumente umbenennen +- [ ] Tags zuweisen + +--- + +## 2026-04-20 16:45:00 + +**Aktion:** Massenumbenennung ALLER verbleibenden Dokumente durchgeführt +**Status:** ✅ Erfolgreich - 56 Dokumente umbenannt +**Details:** +- Verarbeitet in 5 Batches mit Subagents +- Batch 1: 10 Dokumente (inkl. 2 neue Scans) +- Batch 2: 10 Dokumente +- Batch 3: 10 Dokumente +- Batch 4: 10 Dokumente +- Batch 5: 6 Dokumente + +**Gesamtstatus:** +- ✅ 72/72 Dokumente mit AI-Tag versehen +- ✅ Alle Dokumente nach Schema `YYYY-MM-DD_Kategorie_Beschreibung` umbenannt +- ✅ Kategorie-Tags zugewiesen (Bank, Gehalt, Versicherung, Behörde, etc.) + +**Beispiele umbenannter Dokumente:** +- `Scan 2026-04-20 15.38.02` → `2024-07-01_Behoerde_Zeugnis_Grundschule_Jahrgang2b` +- `Girokonto_5429905347_Kontoauszug_20240104` → `2023-12-29_Bank_Kontoauszug_ING_De Dezember_2023` +- `Gutachten 24101109` → `2024-10-07_Versicherung_KFZ_Gutachten_Haftpflichtschaden_VHV` +- `Gehalt Januar 2024` → `2023-01-01_Gehalt_Frankfurt-Entgeltabrechnung-Januar` + +**Nächste Schritte:** +- [x] Alle Dokumente verarbeitet +- [ ] Neue Dokumente automatisch erkennen und verarbeiten + +--- + +## 2026-04-20 17:00:00 + +**Aktion:** Nachbearbeitung - fehlende AI Tags ergänzt +**Status:** ✅ Erfolgreich - 3 Dokumente nachgetragen +**Details:** +- ID 18: RentenInformation 2023 - Andrej Spielmann +- ID 12: Juni 2023 +- ID 20: Protkotoll der orderntlichen Eigentümerversammlung von 21.07.2023 + +**FINALER STATUS:** +- ✅ **72/72 Dokumente** mit AI-Tag versehen +- ✅ **100% vollständig** + +--- + +## 2026-04-20 17:15:00 + +**Aktion:** Manuelle Nachbearbeitung - 7 neue Dokumente verarbeitet +**Status:** ✅ Erfolgreich +**Details:** +- ID 81: CHECK24-Rechnung → 2026-04-20_Rechnung_CHECK24-Rechnung-CP-CMPTBP0-58514997-3 +- ID 80: CHECK24-Rechnung → 2026-04-20_Rechnung_CHECK24-Rechnung-CP-CMPTBP0-58514997-2 +- ID 79: eTicket → 2026-04-20_Rechnung_1765908397841-eTicket-54064538123 +- ID 78: Rechnung → 2026-04-20_Rechnung_Rechnung-2922242876 +- ID 77: RAC → 2026-04-20_Rechnung_RAC-20277959-20260313 +- ID 76: RG → 2026-04-20_Rechnung_RG151140358541 +- ID 75: VSL → 2026-04-20_Versicherung_VSL-2026-121488 + +**Tags zugewiesen:** AI (16) + Rechnung (11) / Versicherung (19) + +**AKTUELLER STATUS:** +- ✅ **79/79 Dokumente** mit AI-Tag versehen +- ✅ **100% vollständig** + +--- + +*Dieses Log wird automatisch bei jeder Paperless-ngx Aktion aktualisiert.* diff --git a/Notes/SQLHosting/Anmeldedaten.md b/Notes/SQLHosting/Anmeldedaten.md new file mode 100644 index 0000000..57eae6d --- /dev/null +++ b/Notes/SQLHosting/Anmeldedaten.md @@ -0,0 +1,25 @@ +# SQL Server Hosting Platform - Anmeldedaten + +## Admin-Zugang +- **Email:** admin@sqlhosting.com +- **Passwort:** Admin123! +- **Rolle:** ADMIN +- **Status:** approved/ACTIVE + +## Customer-Zugang +- **Email:** customer@sqlhosting.com +- **Passwort:** Customer123! +- **Rolle:** CUSTOMER +- **Status:** approved + +## System URLs +- **Frontend:** http://localhost:3000 +- **Backend API:** http://localhost:8000 +- **API Docs:** http://localhost:8000/api/swagger/ + +## Technische Details +- Datenbank: SQL Server (sqlserver_platform) +- Django Backend mit JWT Auth +- Next.js 16 Frontend + +Erstellt am: Monday, April 20, 2026 diff --git a/Notes/SQLHosting/Design.md.md b/Notes/SQLHosting/Design.md.md new file mode 100644 index 0000000..d47339c --- /dev/null +++ b/Notes/SQLHosting/Design.md.md @@ -0,0 +1,623 @@ +# SQL Server Hosting Platform — Design Document + + + +**Session ID:** 2026-04-21-sqlserver-hosting-platform + +**Created:** 2026-04-21T00:00:00Z + +**Status:** Approved + + + +## 1. Overview + + + +The SQL Server Hosting Platform is a full-stack web application that enables admins to manage SQL Server pools (Standalone and High Availability) and allows customers to book database applications hosted on those pools. The platform itself runs in Docker containers; customer SQL Servers are external physical/virtual instances. + + + +### Key Decisions + +- **Platform DB**: Microsoft SQL Server (external at `192.168.101.42:1433`, database: `SQLHostingDesk`). + +- **Customer SQL Server access**: pyodbc exclusively (no ORM). + +- **Dev/Prod separation**: Docker Compose for local dev; the MSSQL platform DB is a shared, external dev instance. + +- **Phased build**: 3 phases (Foundation → Core Platform → Advanced) to manage complexity and allow early feature validation. + + + +--- + + + +## 2. Architecture + + + +``` + +┌──────────────────────────────┐ + +│ Next.js Frontend │ Port 3000 + +│ Admin Panel + Kunden-UI │ + +└─────────────┬────────────────┘ + +│ REST / BFF + +┌─────────────▼────────────────┐ + +│ Django REST API │ Port 8000 + +│ JWT Auth │ API Endpoints │ + +└──────┬──────────────┬────────┘ + +│ │ + +┌──────▼──────┐ ┌────▼─────────────┐ + +│ Redis │ │ MSSQL Platform │ ← External via network + +│ (Celery) │ │ 192.168.101.42 │ + +└──────┬──────┘ └──────────────────┘ + +│ + +┌──────▼──────────────┐ + +│ Celery Workers │ + +│ Discovery │ + +│ Provisioning │ + +│ Health Checks │ + +│ Quota Monitoring │ + +└──────┬──────────────┘ + +│ pyodbc + +┌──────▼─────────────────────────────────┐ + +│ External Customer SQL Server │ + +│ (Unraid: 2x HA, 1x Standalone) │ + +└────────────────────────────────────────┘ + +``` + + + +### Technology Stack + + + +| Layer | Technology | Version | + +|-------------|-------------------------------|---------| + +| Frontend | Next.js (App Router), TS | 16.x | + +| Backend | Django + DRF | 5.2 LTS | + +| Platform DB | MSSQL (external) | 2019+ | + +| Task Queue | Celery + Redis | 5.x | + +| SQL Access | pyodbc | 18.x | + +| Auth | JWT (djangorestframework-simplejwt) | 5.x | + +| Container | Docker + Docker Compose | 24.x | + +| Testing | pytest + Vitest + Playwright | — | + + + +--- + + + +## 3. Phases + + + +| Phase | Content | Deliverable | + +|-------|---------|-------------| + +| **1. Foundation** | Docker-Compose (Django, Redis, Celery, Next.js), Django project setup, Custom User + JWT, Next.js base + auth flow | Platform reachable, login/register works | + +| **2. Core Platform** | ServerPool CRUD, Server Discovery (Celery + pyodbc), IP management, Application/DB models, Standalone booking wizard (incl. DB creation via pyodbc), Admin query log | Customers can book standalone apps; admin sees servers & logs | + +| **3. Advanced** | HA-pair management, AG listener, AG provisioning (real `CREATE AVAILABILITY GROUP` on Unraid HA pair), Soft delete with Celery, DB login management, Quota checks, Audit log | Full platform as per SPEC | + + + +--- + + + +## 4. Database & Django Models + + + +### Database Configuration + + + +```python + +DATABASES = { + +'default': { + +'ENGINE': 'mssql', + +'NAME': 'SQLHostingDesk', + +'USER': 'SQLHosting', + +'PASSWORD': 'Test!123', + +'HOST': '192.168.101.42', + +'PORT': '1433', + +'OPTIONS': { + +'driver': 'ODBC Driver 18 for SQL Server', + +'TrustServerCertificate': 'yes', + +'Encrypt': 'yes', + +}, + +} + +} + +``` + + + +### Django Apps + + + +| App | Models | + +|-----|--------| + +| `user_auth` | `User`, `CustomerProfile` | + +| `serverpool` | `ServerPool`, `Server`, `HAServerPair`, `ServerSelectionThreshold` | + +| `networking` | `IPNetwork`, `IPAddress`, `AGListener` | + +| `applications` | `CustomerApplication`, `CustomerDatabase`, `DatabaseLogin`, `DatabaseQuota` | + +| `provisioning` | `QueryTemplate` | + +| `auditlog` | `QueryLog`, `AuditLog` | + +| `security` | `ElevatedAccount` | + + + +### Core Relationships + + + +- `User` (role: ADMIN/CUSTOMER, is_approved) ↔ `CustomerProfile` (1:1) + +- `CustomerProfile` ↔ `CustomerApplication` (1:N) ↔ `CustomerDatabase` (1:N) + +- `ServerPool` (STANDALONE/HA) ↔ `Server` (1:N) ↔ `HAServerPair` (1:1 for primary, 1:1 for secondary) + +- `HAServerPair` ↔ `AGListener` (1:1) + +- `CustomerDatabase` ↔ `DatabaseLogin` (1:N), `DatabaseQuota` (1:1) + +- `ServerPool` ↔ `ElevatedAccount` (1:1) + + + +### Design Decisions + +- **Soft Delete**: `django-safedelete` library. + +- **Audit Trail**: `django-auditlog` signals + custom `AuditLog` for user actions. + +- **Password Encryption**: `cryptography.fernet` for `ElevatedAccount.password_encrypted`. + +- **Generic FK**: `contenttypes` framework for `IPAddress.assigned_to`. + +- **UUID**: Secure deletion confirmation tokens (`CustomerDatabase.deletion_token`). + + + +--- + + + +## 5. API Design + + + +### Authentication (SimpleJWT) + + + +| Endpoint | Method | Description | + +|----------|--------|-------------| + +| `/api/v1/auth/register/` | POST | Register (status: pending) | + +| `/api/v1/auth/login/` | POST | Login, get tokens | + +| `/api/v1/auth/token/refresh/` | POST | Refresh access token | + +| `/api/v1/auth/me/` | GET | Current user profile | + + + +### Admin APIs (ADMIN role required) + + + +| Endpoint | Method | Description | + +|----------|--------|-------------| + +| `/api/v1/admin/users/pending/` | GET | List unapproved customers | + +| `/api/v1/admin/users/{id}/approve/` | PATCH | Approve customer | + +| `/api/v1/admin/users/{id}/reject/` | PATCH | Reject customer | + +| `/api/v1/admin/server-pools/` | GET, POST | Manage pools | + +| `/api/v1/admin/server-pools/{id}/add-server/` | POST | Add single server | + +| `/api/v1/admin/server-pools/{id}/add-ha-pair/` | POST | Add HA pair | + +| `/api/v1/admin/servers/{id}/discover/` | POST | Trigger discovery task | + +| `/api/v1/admin/servers/{id}/status/` | GET | Get server status | + +| `/api/v1/admin/ip-networks/` | GET, POST | Manage IP networks | + +| `/api/v1/admin/ip-addresses/` | GET | List IPs | + +| `/api/v1/admin/ag-listeners/` | GET, POST | Manage AG listeners | + +| `/api/v1/admin/ha-pairs/{id}/assign-listener/` | PATCH | Assign listener to HA pair | + +| `/api/v1/admin/query-logs/` | GET | Filter query logs | + +| `/api/v1/admin/query-logs/{id}/override/` | PATCH | Set override query | + + + +### Customer APIs (CUSTOMER + is_approved required) + + + +| Endpoint | Method | Description | + +|----------|--------|-------------| + +| `/api/v1/customer/applications/` | GET, POST | List / book apps | + +| `/api/v1/customer/applications/{id}/` | GET | App details | + +| `/api/v1/customer/applications/{id}/databases/` | GET, POST | List / add DBs | + +| `/api/v1/customer/applications/{id}/delete-request/` | DELETE | Request deletion | + +| `/api/v1/customer/applications/{id}/delete-confirm/` | DELETE | Confirm deletion | + +| `/api/v1/customer/databases/{id}/logins/` | GET, POST | Manage DB logins | + + + +### BFF Pattern (Next.js API Routes) + + + +Next.js API Routes act as a proxy to Django to avoid CORS in the browser. + + + +--- + + + +## 6. Celery Tasks & Background Processing + + + +| Task | Trigger | Description | + +|------|---------|-------------| + +| `discover_server` | API call | Run 7 discovery queries via pyodbc, log all queries | + +| `health_check_all` | Celery-Beat (configurable interval) | Check server resources against thresholds | + +| `provision_application` | API call (new app) | Create DB (standalone) or AG+DB+Listener (HA) using Jinja2 templates | + +| `provision_add_database` | API call | Add DB to existing app | + +| `delete_confirmed_application` | Celery-Beat/manual | Execute DROP / AG remove | + +| `delete_confirmed_database` | Celery-Beat/manual | Drop single DB | + +| `check_quota_all` | Celery-Beat (hourly) | Check actual DB size, warn at 80%/95% | + +| `verify_elevated_account` | Celery-Beat (daily) | Verify dbcreator+securityadmin, disable pool on failure | + + + +### Beat Schedule (example) + + + +```python + +CELERY_BEAT_SCHEDULE = { + +'health-check': { + +'task': 'serverpool.tasks.health_check_all', + +'schedule': 300.0, # 5 min (override via config) + +}, + +'quota-check': { + +'task': 'applications.tasks.check_quota_all', + +'schedule': 3600.0, # hourly + +}, + +'account-verify': { + +'task': 'security.tasks.verify_elevated_account', + +'schedule': 86400.0, # daily + +}, + +} + +``` + + + +### Jinja2 Query Templates + + + +All T-SQL queries (Discovery, Provisioning, Delete, Login) are stored as `QueryTemplate` records in the database. Before execution, Celery renders the template with current variables. Admins can override queries via the Query Log admin panel. + + + +--- + + + +## 7. Frontend Architecture + + + +### Next.js App Router Structure + + + +``` + +frontend/ + +├── src/ + +│ ├── app/ + +│ │ ├── (auth)/ # login, register + +│ │ ├── (admin)/ # dashboard, server-pools, servers, ip-networks, + +│ │ │ # ag-listeners, query-logs, users/pending + +│ │ ├── (customer)/ # dashboard, applications, applications/new (wizard), + +│ │ │ # applications/[id] + +│ │ └── api/ # BFF proxy routes + +│ ├── components/ + +│ │ ├── ui/ # shadcn/ui components + +│ │ ├── layout/ # Sidebar, Header + +│ │ ├── forms/ # Reusable forms + +│ │ └── modals/ # Confirmation dialogs + +│ ├── hooks/ + +│ ├── services/ + +│ ├── stores/ + +│ └── lib/ + +├── tests/e2e/ + +├── components.json + +├── tailwind.config.ts + +└── next.config.js + +``` + + + +### State Management + +- **Zustand**: Auth state (user, token, role) + +- **TanStack Query**: Server state (caching, refetching, mutations) + +- **React Hook Form + Zod**: All forms (login, wizard, modals) + + + +### Booking Wizard (5 Steps) + +1. Application name (letters, digits, underscores only) + +2. HA type selection (Standalone vs HA) + +3. Collation (dropdown from discovered server collations) + +4. First database name + size (GB) + +5. Summary + "Book now" + + + +--- + + + +## 8. Docker Compose Setup + + + +### Services (no MSSQL container — external DB) + + + +| Service | Image | Port | + +|---------|-------|------| + +| `django` | Custom (Python 3.12) | 8000 | + +| `celery-worker` | Custom | — | + +| `celery-beat` | Custom | — | + +| `redis` | redis:7-alpine | 6379 | + +| `nextjs` | Custom (Node 22) | 3000 | + + + +### Notes + +- **ODBC Driver 18**: Installed in Django container (`msodbcsql18`). + +- **Debian 12+**: Modern GPG keyring approach (no apt-key). + +- **Volumes**: Django source bind-mount (dev), Redis persistence. + + + +--- + + + +## 9. Security Considerations + + + +- JWT: Access 15min, Refresh 7 days. + +- Service account passwords: Fernet encrypted (key in `.env`). + +- Role-based access: `IsAdmin` / `IsCustomer` + `is_approved` checks. + +- Query override: Admin-only, logged to `AuditLog`. + +- No secrets in Docker images (all via `.env`). + + + +--- + + + +## 10. Exit Criteria (by Phase) + + + +### Phase 1: Foundation + +- [ ] `docker compose up` starts all 5 services without errors. + +- [ ] Django migrations run successfully against MSSQL `SQLHostingDesk`. + +- [ ] JWT login/register API works (tested via curl). + +- [ ] Next.js frontend loads, login page accessible, auth state managed. + +- [ ] ADMIN user can log in and see admin dashboard shell. + + + +### Phase 2: Core Platform + +- [ ] Admin can create Server Pools and add single servers. + +- [ ] Discovery task runs and populates Server fields (RAM, CPU, Disk, Version, Collation). + +- [ ] IP networks can be created and IPs auto-assigned. + +- [ ] Customer can register, admin approves, customer logs in. + +- [ ] Booking wizard (Standalone only) creates a real database on the external SQL Server. + +- [ ] Query log shows all discovery and provisioning queries. + + + +### Phase 3: Advanced + +- [ ] Admin can create HA pairs and assign AG listeners. + +- [ ] HA booking wizard creates a real Availability Group on the Unraid HA pair. + +- [ ] Adding databases to an existing HA app uses `ALTER AVAILABILITY GROUP`. + +- [ ] Soft delete (3-step) works for both applications and databases. + +- [ ] Quota monitoring checks actual sizes and logs warnings. + +- [ ] Audit log records all user actions immutably. + + + +--- + + + +*Design document written and ready for implementation planning.* \ No newline at end of file diff --git a/Notes/SQLHosting/Projektbeschreibung.md b/Notes/SQLHosting/Projektbeschreibung.md new file mode 100644 index 0000000..433ae33 --- /dev/null +++ b/Notes/SQLHosting/Projektbeschreibung.md @@ -0,0 +1,905 @@ +Hier ist die vollständige, finale Projektbeschreibung mit dem Applikationskonzept vollständig integriert: + +*** + +# SQL Server Hosting Platform – Vollständige Technische Projektbeschreibung + +## Technologie-Stack + +| Schicht | Technologie | Version | | +| ------------------ | ------------------------------ | ------- | --- | +| Frontend | Next.js (TypeScript) | 16.x | | +| Backend | Django + Django REST Framework | 5.2 LTS | | +| Plattform-DB | Microsoft SQL Server | 2019+ | | +| Task Queue | Celery + Redis | 5.x | | +| SQL Server Zugriff | pyodbc (ODBC) | 18.x | | +| Auth | JWT (SimpleJWT) | 5.x | | +| Container | Docker + Docker Compose | 24.x | | +| Testing | pytest + Vitest + Playwright | - | | +| CI/CD | GitHub Actions | - | | + +> **Wichtig:** Die Plattformdatenbank ist bewusst mssql – keine SQL Server Eigenabhängigkeit. Alle Verbindungen zu Customer-SQL-Servern laufen ausschließlich über pyodbc. + +*** + +## 📊 Projekt-Status (Aktualisiert: 2026-04-20) + +### ✅ Implementiert + +| Komponente | Status | Details | +|------------|--------|---------| +| Backend Core | ✅ Fertig | Django 5.2, 17 Modelle, alle APIs | +| Frontend Foundation | ✅ Fertig | Next.js 16, 83+ TSX Dateien | +| Docker Setup | ✅ Fertig | 6 Services (mssql, redis, django, celery, celery-beat, nextjs) | +| Authentifizierung | ✅ Fertig | JWT mit simplejwt, Custom User Model | +| Admin Interface | ✅ Fertig | Server Pools, Server, Users, Query Logs | +| Customer Interface | ✅ Fertig | Dashboard, Applications, Booking Wizard | +| API Integration | ✅ Fertig | TanStack Query, BFF Pattern | +| Test Suite | ✅ Fertig | 99+ Backend Tests, Vitest, Playwright E2E | +| Dokumentation | ✅ Fertig | README, API-Docs, Deployment Guide | + +### 🔄 In Entwicklung + +| Komponente | Status | Blocker | +|------------|--------|---------| +| OpenCode Frontend | 🔄 Läuft | Background Task, ~83 Dateien erstellt | + +### 📝 Noch Offen + +- [ ] Produktions-Deployment +- [ ] SSL/TLS Konfiguration +- [ ] Backup-Automatisierung +- [ ] Monitoring & Alerting + +### 📁 Repository Struktur + +``` +sqlserver-platform/ +├── backend/ +│ ├── apps/ +│ │ ├── user_auth/ +│ │ ├── serverpool/ +│ │ ├── applications/ +│ │ ├── provisioning/ +│ │ └── auditlog/ +│ ├── config/ +│ ├── tests/ +│ │ ├── unit/ +│ │ └── integration/ +│ └── requirements.txt +├── frontend/ +│ ├── src/ +│ │ ├── app/ +│ │ │ ├── (admin)/ +│ │ │ ├── (auth)/ +│ │ │ ├── (customer)/ +│ │ │ └── api/ +│ │ ├── components/ +│ │ ├── services/ +│ │ └── lib/ +│ ├── tests/e2e/ +│ └── playwright.config.ts +├── docker-compose.dev.yml +├── docker-compose.yml +├── README.md +├── API_DOCUMENTATION.md +├── DEPLOYMENT.md +└── CONTRIBUTING.md +``` + +### 🐳 Docker Services + +| Service | Port | Beschreibung | +|---------|------|--------------| +| mssql | 1433 | Microsoft SQL Server 2019+ | +| redis | 6379 | Cache & Celery Broker | +| django | 8000 | Django REST API | +| celery-worker | - | Async Task Processing | +| celery-beat | - | Scheduled Tasks | +| nextjs-dev | 3000 | Next.js Frontend | + +### 🔑 Test Credentials + +- **Admin:** admin@sqlhosting.com / Admin123! +- **Backend Health:** http://localhost:8000/api/v1/health/ + +*** + +## Systemarchitektur + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ Next.js Frontend │ +│ Admin Panel │ Kunden Dashboard │ +└───────────────────────────────────┬──────────────────────────────┘ + │ REST API (JWT) +┌───────────────────────────────────▼──────────────────────────────┐ +│ Django REST API │ +│ Auth │ ServerPool │ Application │ Booking │ QueryLog │ AuditLog │ +└──────────────┬────────────────────────────────┬──────────────────┘ + │ │ + ┌──────────▼──────────┐ ┌────────────▼────────────┐ + │ MSSQL │ │ Celery + Redis │ + │ (Plattform-DB) │ │ Discovery, Health, │ + └─────────────────────┘ │ Provisioning, Quota │ + └────────────┬────────────┘ + │ pyodbc + ┌───────────────────▼──────────────────┐ + │ Customer SQL Server │ + │ Standalone │ HA (Always On) │ + └───────────────────────────────────────┘ +``` + +*** + +## Kernkonzept: Applikation als Container + +Das zentrale Objekt aus Kundensicht ist die **Applikation** – nicht die einzelne Datenbank. Eine Applikation ist ein logischer Container, der dem Kunden gehört, einen Namen trägt und einem HA-Typ (Standalone oder High Availability) zugeordnet ist. + +- Bei **Standalone** ist die Applikation eine logische Gruppe von Datenbanken auf demselben Server +- Bei **High Availability** entspricht die Applikation genau **einer Availability Group** – die AG wird nach der Applikation benannt (`AG_`) +- Der Kunde erstellt zuerst eine Applikation (inkl. erster Datenbank), kann danach weitere Datenbanken zur selben Applikation hinzufügen +- Neue Datenbanken einer HA-Applikation werden zur **bestehenden AG hinzugefügt** (`ALTER AVAILABILITY GROUP ... ADD DATABASE`) – es wird keine neue AG erstellt + +*** + +## Modul 1 – Benutzerverwaltung & Authentifizierung + +### Rollen & Aktivierung +Das System kennt zwei Rollen: `ADMIN` und `CUSTOMER`. Ein neuer Kunde registriert sich und landet im Status `is_approved = False` ohne Zugangsberechtigung, bis der Admin ihn manuell freischaltet. Der Admin sieht im Panel alle ausstehenden Aktivierungen mit Registrierungsdatum und kann per Klick freischalten oder ablehnen. + +**Django-Modelle:** +```python +class User(AbstractUser): + role = CharField(choices=["ADMIN", "CUSTOMER"]) + is_approved = BooleanField(default=False) + approved_by = FK(User, null=True) + approved_at = DateTimeField(null=True) + +class CustomerProfile(Model): + user = OneToOneField(User) + company_name = CharField() + contact_phone = CharField() + created_at = DateTimeField() +``` + +**API-Endpunkte:** +``` +POST /api/auth/register/ +POST /api/auth/login/ +POST /api/auth/token/refresh/ +GET /api/admin/users/pending/ +PATCH /api/admin/users/{id}/approve/ +PATCH /api/admin/users/{id}/reject/ +``` + +*** + +## Modul 2 – Server Pool Management + +### Pool-Typen +Der Admin verwaltet Server in Pools. Es gibt zwei Typen: + +**Typ 1 – Standalone:** Ein einzelner SQL Server. Datenbanken laufen ohne Redundanz. + +**Typ 2 – High Availability:** Zwei SQL Server werden als Pärchen hinzugefügt (Primary/Secondary in einer Always On Availability Group). Primär und Sekundär können nach Failover wechseln. + +**Django-Modelle:** +```python +class ServerPool(Model): + name = CharField() + pool_type = CharField(choices=["STANDALONE", "HA"]) + description = TextField() + is_active = BooleanField() + created_at = DateTimeField() + +class Server(Model): + pool = FK(ServerPool) + hostname = CharField() + ip_address = FK(IPAddress) + sql_instance = CharField() # z.B. HOSTNAME\INSTANCE + # Discovery – Ressourcen + total_ram_gb = DecimalField() + total_cpu_cores = IntegerField() + total_disk_gb = DecimalField() + free_ram_gb = DecimalField() + free_disk_gb = DecimalField() + # Discovery – SQL Server Version + sql_version_full = CharField() # z.B. 16.0.4135.4 + sql_version_major = IntegerField() # z.B. 16 + sql_version_level = CharField() # RTM / CU14 / SP1 + sql_edition = CharField() # Enterprise / Standard + # Discovery – HA-relevante Felder + collation = CharField() + endpoint_name = CharField() + endpoint_port = IntegerField() + server_fqdn = CharField() + # Status + last_checked = DateTimeField() + is_active = BooleanField() + discovery_status = CharField() # PENDING / OK / ERROR + +class HAServerPair(Model): + pool = FK(ServerPool) + primary_server = FK(Server) + secondary_server = FK(Server) + listener = FK(AGListener) # muss Admin vorher zuweisen + is_ready = BooleanField() # True wenn Listener zugewiesen +``` + +**API-Endpunkte:** +``` +GET /api/admin/server-pools/ +POST /api/admin/server-pools/ +POST /api/admin/server-pools/{id}/add-server/ +POST /api/admin/server-pools/{id}/add-ha-pair/ +POST /api/admin/servers/{id}/discover/ +GET /api/admin/servers/{id}/status/ +``` + +*** + +## Modul 3 – Server Discovery + +Beim Hinzufügen eines Servers wird sofort ein Celery-Task ausgelöst. Alle dabei ausgeführten SQL-Queries werden automatisch im **Query Log** gespeichert. + +### Discovery-Queries + +```sql +-- 1. RAM & CPU +SELECT physical_memory_kb / 1024 / 1024 AS total_ram_gb, + cpu_count +FROM sys.dm_os_sys_info; + +-- 2. Disk Space +SELECT volume_mount_point, + total_bytes / 1073741824.0 AS total_gb, + available_bytes / 1073741824.0 AS free_gb +FROM sys.dm_os_volume_stats(DB_ID('master'), 1); + +-- 3. SQL Server Version +SELECT SERVERPROPERTY('ProductVersion') AS version_full, + SERVERPROPERTY('ProductMajorVersion') AS version_major, + SERVERPROPERTY('ProductLevel') AS version_level, + SERVERPROPERTY('Edition') AS edition; + +-- 4. Collation +SELECT SERVERPROPERTY('Collation') AS server_collation; + +-- 5. HA Endpoint (nur für HA-Server) +SELECT name, port +FROM sys.tcp_endpoints +WHERE type_desc = 'DATABASE_MIRRORING'; + +-- 6. Server FQDN +SELECT SERVERPROPERTY('MachineName') AS machine_name, + DEFAULT_DOMAIN() AS domain; + +-- 7. IP-Adresse (zur Zuordnung in IP-Pool) +SELECT local_net_address +FROM sys.dm_exec_connections +WHERE session_id = @@SPID; +``` + +### SQL Server Version → Anzeigename & AG-Template + +| `version_major` | Anzeigename | AG-Template | +|---|---|---| +| 13 | SQL Server 2016 | `BASIC` Availability Groups | +| 14 | SQL Server 2017 | Standard AG | +| 15 | SQL Server 2019 | Standard AG | +| 16 | SQL Server 2022 | Standard AG / `CONTAINED` AG möglich | + +Die Major-Version steuert welches Query-Template bei der AG-Erstellung verwendet wird. Templates werden pro `query_type` + `sql_version_major` in der Datenbank hinterlegt. + +*** + +## Modul 4 – Scheduled Health Checks + +Ein Celery-Beat-Task prüft regelmäßig alle aktiven Server auf freie Ressourcen. Intervall ist im Admin konfigurierbar. + +### Admin-Konfiguration: `ServerSelectionThreshold` + +```python +class ServerSelectionThreshold(Model): + server_pool = FK(ServerPool) + min_free_disk_gb = DecimalField() # z.B. 10 GB müssen frei sein + min_free_ram_gb = DecimalField() + min_free_cpu_percent = IntegerField() # z.B. 20 % CPU muss frei sein + health_check_interval = IntegerField() # Minuten +``` + +Beim Buchungsprozess wählt das System automatisch den passenden Server/Pair anhand dieser Schwellwerte. Ein Server, der die Thresholds nicht erfüllt, wird automatisch aus der Auswahl ausgeschlossen. + +*** + +## Modul 5 – IP-Adresspool + +Der Admin legt Netzwerke manuell an. IPs werden bei der Server-Discovery automatisch dem passenden Netz zugeordnet. Für AG-Listener wird die nächste freie IP aus dem passenden Netz automatisch vergeben. + +```python +class IPNetwork(Model): + cidr = CIDRField() # z.B. 10.10.1.0/24 + description = CharField() + vlan_id = IntegerField(null=True) + created_at = DateTimeField() + +class IPAddress(Model): + address = GenericIPAddressField() + network = FK(IPNetwork) + purpose = CharField(choices=["SERVER", "LISTENER", "FREE"]) + assigned_to_type = CharField(null=True) # GenericFK + assigned_to_id = IntegerField(null=True) + reserved_at = DateTimeField(null=True) +``` + +**API-Endpunkte:** +``` +GET /api/admin/ip-networks/ +POST /api/admin/ip-networks/ +POST /api/admin/ip-networks/{id}/add-ip-range/ +GET /api/admin/ip-addresses/ +PATCH /api/admin/ip-addresses/{id}/assign/ +``` + +*** + +## Modul 6 – AG-Listener Verwaltung + +Bevor ein HA-ServerPair für Kundenbuchungen verfügbar ist, **muss der Admin einen Listener zuweisen**. Erst dann wird `HAServerPair.is_ready = True`. Der Listener besteht aus Name, IP (aus dem IP-Pool) und Port. + +```python +class AGListener(Model): + name = CharField() # z.B. AG-LISTENER-01 + ip_address = FK(IPAddress) + port = IntegerField() # Standard: 1433 + subnet_mask = CharField() + assigned_to = OneToOneField(HAServerPair, null=True) + created_at = DateTimeField() +``` + +**API-Endpunkte:** +``` +GET /api/admin/ag-listeners/ +POST /api/admin/ag-listeners/ +PATCH /api/admin/ha-pairs/{id}/assign-listener/ +``` + +*** + +## Modul 7 – Service Account Isolation + +Pro **ServerPool** wird ein dedizierter Service Account konfiguriert. Ein kompromittierter Account hat damit nur Zugriff auf seinen eigenen Pool. Das Passwort wird mit **Fernet-Verschlüsselung** gespeichert (Key in `.env`). + +```python +class ElevatedAccount(Model): + server_pool = OneToOneField(ServerPool) + username = CharField() + password_encrypted = BinaryField() # Fernet encrypted + last_verified = DateTimeField() + permissions_ok = BooleanField() # dbcreator + securityadmin + last_permission_check = DateTimeField() +``` + +Ein Celery-Task prüft regelmäßig ob der Account noch `dbcreator` und `securityadmin` besitzt. Fehlt die Berechtigung, wird der Pool für neue Buchungen gesperrt und ein Audit-Log-Eintrag erstellt. + +*** + +## Modul 8 – Kundenbuchungsprozess + +### Konzept: Applikation → Datenbank + +``` +Applikation (Container) +├── Name: "MeinShop" ← vom Kunden vergeben +├── Typ: HA ← bestimmt AG-Name: AG_MeinShop +├── Server/Pair: automatisch ← anhand Thresholds gewählt +├── Collation: SQL_Latin1_... ← aus Discovery des gewählten Pools +│ +├── Datenbank: "MeinShop_Prod" ← erste DB bei Erstellung +├── Datenbank: "MeinShop_Dev" ← später hinzugefügt +└── Datenbank: "MeinShop_Log" ← später hinzugefügt +``` + +### Buchungs-Wizard – Neue Applikation (5 Schritte) + +``` +Schritt 1: Applikationsname + ┌─────────────────────────────────────────────┐ + │ Applikationsname: [ MeinShop ] │ + │ (Nur Buchstaben, Zahlen, Unterstriche; │ + │ wird Teil des AG-Namens bei HA) │ + └─────────────────────────────────────────────┘ + +Schritt 2: HA-Typ wählen + ┌───────────────────┐ ┌──────────────────────────────┐ + │ Standalone │ │ High Availability │ + │ SQL Server 2022 │ │ SQL Server 2022 Enterprise │ + │ Standard │ │ Always On – 2 Nodes │ + └───────────────────┘ └──────────────────────────────┘ + +Schritt 3: SQL Server Collation wählen + Dropdown – befüllt aus discovered Collations der + verfügbaren Server im gewählten Pool-Typ + +Schritt 4: Name der ersten Datenbank + Größe (GB) + ┌─────────────────────────────────────────────┐ + │ Datenbankname: [ MeinShop_Prod ] │ + │ Größe (GB): [ 10 ] │ + └─────────────────────────────────────────────┘ + +Schritt 5: Zusammenfassung + "Jetzt buchen" + Anzeige: Applikation, Typ, Collation, Version, + DB-Name, Größe, ggf. AG-Name +``` + +### Weitere Datenbank zu bestehender Applikation hinzufügen + +``` +Kunde → "Applikation MeinShop" → "Neue Datenbank hinzufügen" + +Schritt 1: Datenbankname + Größe +Schritt 2: Zusammenfassung + "Erstellen" + +→ Bei HA: ALTER AVAILABILITY GROUP [AG_MeinShop] ADD DATABASE [neuer_name] +→ Bei Standalone: CREATE DATABASE auf demselben Server wie die Applikation +``` + +### Django-Modelle + +```python +class CustomerApplication(Model): + customer = FK(CustomerProfile) + app_name = CharField() # z.B. "MeinShop" + ha_type = CharField(choices=["STANDALONE", "HA"]) + # Zugewiesener Server/Pair (bei Erstellung automatisch gewählt) + server = FK(Server, null=True) # Standalone + ha_pair = FK(HAServerPair, null=True) # HA + collation = CharField() + # HA-spezifisch + ag_name = CharField(null=True) # = "AG_" + app_name + ag_created = BooleanField(default=False) + # Status + status = CharField(choices=[ + "PROVISIONING", "ACTIVE", + "ERROR", "DELETED" + ]) + created_at = DateTimeField() + deleted_at = DateTimeField(null=True) + +class CustomerDatabase(Model): + application = FK(CustomerApplication) # ← Neu: gehört zur App + customer = FK(CustomerProfile) + db_name = CharField() + size_gb = IntegerField() + connection_string = CharField() + # Soft Delete + status = CharField(choices=[ + "ACTIVE", "DELETION_REQUESTED", + "DELETION_CONFIRMED", "DELETED" + ]) + created_at = DateTimeField() + deleted_at = DateTimeField(null=True) + deletion_confirmed_at = DateTimeField(null=True) + drop_executed_at = DateTimeField(null=True) + deletion_token = UUIDField(null=True) +``` + +### Automatische Serverauswahl (Backend-Logik) + +```python +def select_server(pool_type, required_size_gb, collation): + threshold = ServerSelectionThreshold.objects.get( + server_pool__pool_type=pool_type + ) + candidates = Server.objects.filter( + pool__pool_type=pool_type, + collation=collation, + free_disk_gb__gte=required_size_gb + threshold.min_free_disk_gb, + free_ram_gb__gte=threshold.min_free_ram_gb, + is_active=True + ).order_by('free_disk_gb') # kleinsten passenden nehmen + return candidates.first() +``` + +**API-Endpunkte:** +``` +GET /api/customer/applications/ +POST /api/customer/applications/ # neue App + erste DB +GET /api/customer/applications/{id}/ +POST /api/customer/applications/{id}/databases/ # weitere DB hinzufügen +GET /api/customer/applications/{id}/databases/ +DELETE /api/customer/applications/{id}/delete-request/ +DELETE /api/customer/applications/{id}/delete-confirm/ +``` + +*** + +## Modul 9 – Datenbankbereitstellung (Celery-Task) + +Nach Buchung läuft ein Celery-Task, der via pyodbc alle nötigen SQL-Schritte ausführt. Alle Queries werden im Query Log gespeichert. + +### Fall A: Neue Standalone-Applikation (erste DB) + +```sql +CREATE DATABASE [{{db_name}}] COLLATE {{collation}}; +ALTER DATABASE [{{db_name}}] + MODIFY FILE (NAME = '{{db_name}}', SIZE = {{size_gb}}GB); +``` + +### Fall B: Weitere DB zu bestehender Standalone-Applikation + +```sql +-- Auf demselben Server wie die Applikation +CREATE DATABASE [{{db_name}}] COLLATE {{collation}}; +ALTER DATABASE [{{db_name}}] + MODIFY FILE (NAME = '{{db_name}}', SIZE = {{size_gb}}GB); +``` + +### Fall C: Neue HA-Applikation (AG + erste DB) + +```sql +-- 1. DB auf Primary erstellen +CREATE DATABASE [{{db_name}}] COLLATE {{collation}}; + +-- 2. Recovery Model setzen (AG-Voraussetzung) +ALTER DATABASE [{{db_name}}] SET RECOVERY FULL; + +-- 3. Full Backup (AG-Voraussetzung) +BACKUP DATABASE [{{db_name}}] + TO DISK = '\\{{backup_share}}\{{db_name}}.bak' + WITH FORMAT, INIT; + +-- 4. Availability Group erstellen (SQL 2022 Template) +CREATE AVAILABILITY GROUP [AG_{{app_name}}] +WITH ( + DB_FAILOVER = ON, + REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT = 0 +) +FOR DATABASE [{{db_name}}] +REPLICA ON + '{{primary_fqdn}}' WITH ( + ENDPOINT_URL = 'TCP://{{ep1_host}}:{{ep1_port}}', + AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, + FAILOVER_MODE = AUTOMATIC, + SEEDING_MODE = AUTOMATIC, + SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL) + ), + '{{secondary_fqdn}}' WITH ( + ENDPOINT_URL = 'TCP://{{ep2_host}}:{{ep2_port}}', + AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, + FAILOVER_MODE = AUTOMATIC, + SEEDING_MODE = AUTOMATIC, + SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL) + ); + +-- 5. Listener hinzufügen +ALTER AVAILABILITY GROUP [AG_{{app_name}}] +ADD LISTENER '{{listener_name}}' ( + WITH IP (('{{listener_ip}}', '{{subnet_mask}}')), + PORT = {{listener_port}} +); + +-- 6. Secondary joinen (läuft auf Secondary-Server) +ALTER AVAILABILITY GROUP [AG_{{app_name}}] JOIN; +ALTER AVAILABILITY GROUP [AG_{{app_name}}] + GRANT CREATE ANY DATABASE; +``` + +### Fall D: Weitere DB zu bestehender HA-Applikation + +```sql +-- 1. DB auf Primary erstellen (AG bereits vorhanden) +CREATE DATABASE [{{db_name}}] COLLATE {{collation}}; +ALTER DATABASE [{{db_name}}] SET RECOVERY FULL; + +-- 2. Backup +BACKUP DATABASE [{{db_name}}] + TO DISK = '\\{{backup_share}}\{{db_name}}.bak' + WITH FORMAT, INIT; + +-- 3. Zur bestehenden AG hinzufügen +ALTER AVAILABILITY GROUP [AG_{{app_name}}] + ADD DATABASE [{{db_name}}]; +-- Kein neuer Listener nötig – AG_{{app_name}} existiert bereits +``` + +*** + +## Modul 10 – Soft Delete (Doppelte Bestätigung) + +Das Löschen kann auf zwei Ebenen erfolgen: **Einzelne Datenbank** oder **gesamte Applikation**. Beide Ebenen durchlaufen den gleichen 3-stufigen Prozess. + +``` +STUFE 1 – Löschanfrage + Kunde klickt "Löschen" (DB oder App) + → Modal: "Bitte Namen eingeben zur Bestätigung" + → POST /api/customer/applications/{id}/delete-request/ + oder POST /api/customer/databases/{id}/delete-request/ + → Status: DELETION_REQUESTED + → Karenzzeit: deleted_at = jetzt + 48h + → Audit Log: DELETE_REQUEST + → E-Mail (wenn aktiv): Bestätigungs-Link + +STUFE 2 – Finale Bestätigung + Dashboard-Banner: "Löschung ausstehend" + Kunde klickt "Endgültig löschen" + gibt Namen erneut ein + → POST .../delete-confirm/ + → Status: DELETION_CONFIRMED + → Audit Log: DELETE_CONFIRMED + +STUFE 3 – Celery-Task Ausführung + → Einzelne DB Standalone: + DROP DATABASE [{{db_name}}] + + → Einzelne DB HA: + ALTER AVAILABILITY GROUP [AG_{{app_name}}] + REMOVE DATABASE [{{db_name}}]; + DROP DATABASE [{{db_name}}]; + + → Gesamte Applikation HA: + DROP AVAILABILITY GROUP [AG_{{app_name}}]; + DROP DATABASE [{{db_name}}]; -- für jede DB der App + + → Status: DELETED, drop_executed_at = timestamp + → Audit Log: DELETE_EXECUTED + → E-Mail (wenn aktiv): Bestätigung der Löschung +``` + +*** + +## Modul 11 – Login-Verwaltung (Kundenseite) + +Der Kunde kann für seine Datenbanken SQL Server Logins erstellen und verwalten. Alle Aktionen laufen über den `ElevatedAccount` des zugehörigen Pools. Der Kunde sieht ausschließlich Datenbanken und Logins, die seiner `CustomerProfile`-ID gehören. + +```sql +-- Login erstellen +CREATE LOGIN [{{login_name}}] + WITH PASSWORD = '{{password}}', + DEFAULT_DATABASE = [{{db_name}}]; + +USE [{{db_name}}]; +CREATE USER [{{login_name}}] FOR LOGIN [{{login_name}}]; +ALTER ROLE db_datareader ADD MEMBER [{{login_name}}]; +ALTER ROLE db_datawriter ADD MEMBER [{{login_name}}]; + +-- Login löschen +USE [{{db_name}}]; +DROP USER [{{login_name}}]; +USE master; +DROP LOGIN [{{login_name}}]; +``` + +**Django-Modell `DatabaseLogin`:** +```python +class DatabaseLogin(Model): + database = FK(CustomerDatabase) + login_name = CharField() + created_at = DateTimeField() + created_by = FK(User) + is_active = BooleanField() +``` + +**API-Endpunkte:** +``` +GET /api/customer/databases/{id}/logins/ +POST /api/customer/databases/{id}/logins/ +DELETE /api/customer/databases/{id}/logins/{login_id}/ +``` + +*** + +## Modul 12 – Query Log & Admin Panel + +Jede T-SQL-Abfrage wird geloggt. Der Admin kann fehlerhafte Queries per Override korrigieren ohne Code-Deployment. + +```python +class QueryLog(Model): + server = FK(Server) + executed_by = CharField() # "system" / "admin" / username + query_type = CharField(choices=[ + "DISCOVERY", "HEALTH_CHECK", + "DB_CREATE", "DB_DROP", + "LOGIN_CREATE", "LOGIN_DROP", + "AG_CREATE", "AG_DROP", "AG_ADD_DB", "AG_REMOVE_DB", + "QUOTA_CHECK", "PERMISSION_CHECK" + ]) + query_text = TextField() # tatsächlich ausgeführte Query + executed_at = DateTimeField() + duration_ms = IntegerField() + success = BooleanField() + error_message = TextField(null=True) + # Override-System + is_overridden = BooleanField(default=False) + override_query = TextField(null=True) + override_by = FK(User, null=True) + override_at = DateTimeField(null=True) +``` + +**Override-Logik:** Das System prüft vor jeder Ausführung ob eine Override-Query existiert und nutzt diese bevorzugt. Damit kann der Admin z. B. eine fehlerhafte AG-Query reparieren ohne Deployment. + +**API-Endpunkte:** +``` +GET /api/admin/query-logs/ +GET /api/admin/query-logs/?type=AG_CREATE&success=false +PATCH /api/admin/query-logs/{id}/override/ +DELETE /api/admin/query-logs/{id}/override/ +``` + +*** + +## Modul 13 – Quota-Monitoring + +Ein Celery-Beat-Task prüft regelmäßig die tatsächliche Datenbankgröße und vergleicht sie mit dem gebuchten Wert. Warnungen werden geloggt, E-Mails nur wenn `QUOTA_WARNINGS_ENABLED = True`. + +```sql +SELECT + d.name, + SUM(mf.size) * 8 / 1024.0 AS current_size_mb +FROM sys.databases d +JOIN sys.master_files mf ON d.database_id = mf.database_id +WHERE d.name = '{{db_name}}' +GROUP BY d.name; +``` + +```python +class DatabaseQuota(Model): + database = OneToOneField(CustomerDatabase) + booked_size_gb = DecimalField() + current_size_gb = DecimalField() + last_checked = DateTimeField() + warning_threshold_percent = IntegerField(default=80) + critical_threshold_percent = IntegerField(default=95) + last_warning_sent = DateTimeField(null=True) +``` + +*** + +## Modul 14 – E-Mail-System (vorbereitet, deaktiviert) + +```python +# settings.py +EMAIL_NOTIFICATIONS_ENABLED = False +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +# Aktivierung: EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" + +def send_notification(template, recipient, context): + if not settings.EMAIL_NOTIFICATIONS_ENABLED: + return # Nur loggen, nicht senden +``` + +**Vorbereitete E-Mail-Events:** +- Kundenkonto freigeschaltet / abgelehnt +- Applikation / Datenbank erfolgreich erstellt +- Erstellungsfehler (an Admin + Kunde) +- Löschanfrage Stufe 1 (Bestätigungs-Link) +- Löschung ausgeführt +- Quota-Warnung 80% / 95% +- Service Account Berechtigung fehlt (an Admin) +- Passwort-Reset + +*** + +## Modul 15 – Audit Log + +Das Audit Log protokolliert Benutzeraktionen getrennt vom Query Log. Es ist **read-only** und unveränderbar. + +```python +class AuditLog(Model): + actor = FK(User) + action = CharField(choices=[ + "APP_CREATE", "APP_DELETE_REQUEST", + "APP_DELETE_CONFIRMED", "APP_DELETE_EXECUTED", + "DB_CREATE", "DB_DELETE_REQUEST", + "DB_DELETE_CONFIRMED", "DB_DELETE_EXECUTED", + "LOGIN_CREATE", "LOGIN_DELETE", + "CUSTOMER_APPROVED", "CUSTOMER_REJECTED", + "SERVER_ADDED", "SERVER_REMOVED", + "POOL_CREATED", "AG_CREATED", + "LISTENER_ASSIGNED", "IP_ASSIGNED", + "QUOTA_WARNING", "PERMISSION_FAIL", + "QUERY_OVERRIDE", "EMAIL_SENT" + ]) + target_type = CharField() + target_id = IntegerField() + target_repr = CharField() # Snapshot: "App: MeinShop / customer@example.com" + ip_address = GenericIPAddressField() + timestamp = DateTimeField(auto_now_add=True) + extra_data = JSONField(null=True) +``` + +*** + +## Benötigte Pakete – Vollständige Liste + +### Backend (Python/Django) + +| Paket | Zweck | +| ------------------------------- | ------------------------------------------------ | +| `django` | Web Framework | +| `djangorestframework` | REST API | +| `mssql-django` | Optionaler SQL Server ORM Support | +| `pyodbc` | Verbindung zu Customer SQL Servern | +| `celery` | Async Task Queue | +| `celery[redis]` | Redis als Broker | +| `django-celery-beat` | Scheduled/Periodic Tasks | +| `django-celery-results` | Task-Ergebnisse in DB | +| `djangorestframework-simplejwt` | JWT Authentication | +| `django-cors-headers` | CORS für Next.js | +| `django-filter` | Filterbare API-Endpunkte | +| `drf-spectacular` | OpenAPI / Swagger Dokumentation | +| `cryptography` | Fernet-Verschlüsselung für Service Accounts | +| `netaddr` | IP-Adress- und Netzwerkberechnung | +| `python-decouple` | `.env` Feature-Flags & Config | +| `psycopg2-binary` | mssql Plattform-DB | +| `django-anymail` | E-Mail (vorbereitet, deaktiviert) | +| `django-safedelete` | Soft-Delete Mixin | +| `django-auditlog` | Audit Logging via Signals | +| `Jinja2` | Dynamische Query-Templates (`{{app_name}}` etc.) | +| `redis` | Redis-Client für Cache & Celery | + +### Frontend (Next.js / TypeScript) + +| Paket | Zweck | +|---|---| +| `next-auth` | Authentifizierung + Session | +| `@tanstack/react-query` | Server State & Data Fetching | +| `axios` | HTTP Client | +| `zustand` | State Management | +| `react-hook-form` | Formulare (Buchungs-Wizard, Bestätigungs-Modals) | +| `zod` | Schema-Validierung Frontend + API | +| `shadcn/ui` | UI-Komponentenbibliothek | +| `tailwindcss` | Styling | +| `@tanstack/react-table` | Admin-Tabellen (Server Pool, Query Log, Kunden) | +| `lucide-react` | Icons | +| `sonner` | Toast-Notifications | +| `@codemirror/react` + `@uiw/react-codemirror` | SQL Query-Editor im Admin (Query Override) | +| `date-fns` | Datums-Formatierung | +| `recharts` | Ressourcen-Charts im Admin (CPU/RAM/Disk) | + +*** + +## Ergänzende technische Hinweise + +### Docker Learnings + +- **ODBC Treiber** muss auf dem Django-Server installiert sein: `msodbcsql18` (Linux/Docker) +- **Apt-Key deprecated** in Debian 12+ - moderner GPG-Keyring Approach verwenden +- **Django App Name Conflicts** - Custom `auditlog` app vs `django-auditlog` Package +- **Jinja2 Dependency** - Wird von Django für Templates benötigt, aber oft vergessen +- **Build Cache** - Bei Dependency-Änderungen `--no-cache` verwenden: `docker compose up -d --build --no-cache` + +### Frontend Architektur + +- **Next.js 16 App Router** mit React 19 +- **TanStack Query** für Server State Management +- **Zustand** für Client State +- **BFF Pattern** - API Routes als Proxy zu Django (vermeidet CORS) +- **shadcn/ui + Tailwind** für UI-Komponenten +- **Zod** für Schema-Validierung + +### Testing Strategy + +- **Backend:** pytest mit 99+ Tests (Models, Serializers, Services) +- **Frontend:** Vitest für Unit Tests, Playwright für E2E +- **Coverage Target:** 80%+ für Core Functionality +- **Test Database:** SQLite für schnelle Unit Tests + +### Security + +- **JWT Tokens:** Access 15min, Refresh 7 Tage +- **Fernet Encryption** für Service Account Passwörter +- **Soft Delete** mit django-safedelete (3-stufige Bestätigung) +- **Audit Logging** für alle Benutzeraktionen + +### Bekannte Hürden + +| Problem | Lösung | +|---------|--------| +| apt-key deprecated | GPG Keyring statt apt-key verwenden | +| App Name Konflikt | Nur custom app in INSTALLED_APPS | +| Django Admin 500 | Jinja2 Backend entfernen, nur DjangoTemplates | +| Migration Konflikte | `token_blacklist` aus INSTALLED_APPS entfernen | +| CORS Issues | BFF Pattern mit API Routes | + +*** diff --git a/Notes/Trainings/.DS_Store b/Notes/Trainings/.DS_Store new file mode 100644 index 0000000..f7b9b44 Binary files /dev/null and b/Notes/Trainings/.DS_Store differ diff --git a/Notes/Trainings/Erwachsene/2026-03-03.md b/Notes/Trainings/Erwachsene/2026-03-03.md new file mode 100644 index 0000000..6806594 --- /dev/null +++ b/Notes/Trainings/Erwachsene/2026-03-03.md @@ -0,0 +1,13 @@ +## Training (Erwachsene) – 2026-03-03 + +**Trainer:** Gabor (hat das Training übernommen) + +**Inhalt:** +- Coole Sachen mit Rumreißer gezeigt +- Als Verteidigungstechnik + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file diff --git a/Notes/Trainings/Kinder/2026-03-03.md b/Notes/Trainings/Kinder/2026-03-03.md new file mode 100644 index 0000000..ea48ed4 --- /dev/null +++ b/Notes/Trainings/Kinder/2026-03-03.md @@ -0,0 +1,18 @@ +## Kindertraining – 2026-03-03 + +**Trainer:** Andrej (ich) + +**Inhalt:** +- +- + +**Besonderes:** +- Ein Mädchen (5 Jahre) hat sich an der Augenbraue verletzt, Wunde ist aufgegangen (ca. 3-4 cm). +- Sie war gestern neu mit ihren Geschwistern (insgesamt 5 Kinder). +- Die Familie hat Anmeldezettel abgegeben (muss noch abgegeben werden). + +**Notizen:** +- +- + +**Weitere Eindrücke:** \ No newline at end of file