MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 16s
259 lines
15 KiB
Markdown
259 lines
15 KiB
Markdown
# 🧠 BRAIN.md — MindSift Internal Dev Cheat Sheet & Architecture Log
|
||
> **Zweck:** Zentrales, technisch präzises Nachschlagewerk für Entwicklung, Design-System, Datenbank-Architektur, State-Machines, Backend-APIs und aktuellen Umsetzungsstand. Wird bei jeder Änderung aktuell gehalten.
|
||
|
||
---
|
||
|
||
## 📋 0. Aktueller Projektplan & Umsetzungs-Status
|
||
|
||
### Erledigte Meilensteine
|
||
- [x] **CI/CD & Multi-Stage Docker Build (`Dockerfile`, `.gitea/workflows/build-and-publish.yaml`):**
|
||
- Alle Frontend-Checks (`tsc --noEmit`, `vite build`) und Backend-Tests (`go test -v ./...`) laufen direkt im Multi-Stage Container-Build ohne doppelte Vorab-Steps.
|
||
- Push auf `dev` baut und überschreibt `git.dsit.me/sandrodach/mindsift:dev`.
|
||
- [x] **Monochromes, eckiges Desktop-Design & Zentrale CSS-Steuerung (`frontend/src/index.css`, `frontend/tailwind.config.js`):**
|
||
- Zentrale CSS-Variablen (`--bg-app`, `--bg-panel`, `--accent-primary`, `--border-color`, `--radius-card: 0px`) in `frontend/src/index.css`.
|
||
- Eckige Tabs, eckige Panels/Karten (`0px` Radius), nur die Schnell-Eingabe-Blase oben im Header und Profil-Avatare sind rund (`rounded-full`).
|
||
- Untere doppelte Navigationsleiste entfernt; Schnell-Notiz-Blase (`InstantCaptureBar`) sitzt ganz oben im Header über den Tabs.
|
||
- Workspace links als eckiger, monochromer Datei-/Ordner-Baum (File Explorer Tree mit Unterordnern).
|
||
- [x] **Quick Dump Notes + Triage Swipen + Direkt-Zuordnung (`InstantCaptureBar.tsx`, `TriageDeck.tsx`, `server.go`):**
|
||
- **Quick Dump (Standard):** Text oben eingeben + `Enter` wirft den Gedanken sofort als Quick Dump (`status: 'inbox'`) ins Triage-Deck.
|
||
- **Direkt-Zuordnung bei der Eingabe:** Über das Auswahl-Menü (`Quick Dump ▾`) in der Eingabeleiste kann ein Eintrag alternativ direkt als **Aufgabe** (`target: 'task'`), direkt auf eine **Geteilte Liste** (`target: 'list_item'`) oder direkt als **Workspace-Notiz** (`target: 'document'`) gespeichert werden.
|
||
- **Triage-Entscheidung & Warteschlange:** Im Triage-Deck per Swipe/Klick **Wegwerfen** (`←` / `H`), **Später** (`Leertaste` – ans Ende schieben), **Auf Liste**, **Als Notiz** (`D`), **Mit Termin** (`T`) oder **Als Aufgabe** (`→` / `L`). Inklusive Inline-Textbearbeitung und Warteschlangen-Liste unter der aktiven Karte.
|
||
- [x] **Mehrere Aufgabenlisten & Kanban-Board (`TasksView.tsx`, `models.go`, `sqlite.go`):**
|
||
- Beliebig viele Aufgabenlisten (z.B. Privat, Arbeit, Homelab) mit eigenem Farb- und Icon-Code.
|
||
- Teilen von Aufgabenlisten mit Teams/Spaces.
|
||
- Umschalter zwischen kompakter Listenansicht und 3-Spalten-Kanban-Board (`Zu erledigen`, `In Arbeit`, `Erledigt`) mit 1-Klick-Statuswechsel, Fälligkeitsdaten und Notizfeldern.
|
||
- [x] **Benutzer- & Rollensteuerung + Admin-Panel (`SettingsModal.tsx`, `server.go`, `sqlite.go`):**
|
||
- Benutzerrollen: `admin` und `member`.
|
||
- Admin-Verwaltungsbereich zur Erstellung von Accounts, Rollenwechsel, Deaktivierung und Passwort-Reset.
|
||
- Server-weite Sharing-Isolations-Modi: `open` (alle finden sich), `teams_only` (nur innerhalb von Teams/Organisationen sichtbar) und `strict_private` (kein Teilen).
|
||
- Organisationen & Teams zur Strukturierung von Benutzergruppen.
|
||
- [x] **Workspace Markdown-Wiki mit Notion-/Word-Toolbar & Disk-Sync (`WorkspaceView.tsx`, `sqlite.go`):**
|
||
- Word-/Notion-artige Formatierungsleiste oben (Fett, Kursiv, Durchgestrichen, Code, Links, Überschriften H1-H3, Checklisten, Aufzählungen, Callouts, Markdown-Tabellen, Fortschrittsbalken).
|
||
- Interne Wiki-Links (`[[Dokumenttitel]]`) mit Klick-Navigation und Auto-Erkennung.
|
||
- Physische Speicherung auf Festplatte unter `/data/workspace/<user_id>/...` als Standard-`.md`-Dateien zur einfachen Daten-Extraktion, synchronisiert mit SQLite.
|
||
- [x] **Webhooks, Alarm-Scheduler & Discord-Integration (`SettingsModal.tsx`, `server.go`):**
|
||
- "Webhooks & Export" aus dem Header in das Benutzereinstellungen-Modal verlagert.
|
||
- Eigene API-Tokens mit curl-Beispielen für Skripte und externe Alarme.
|
||
- Hintergrund-Goroutine im Backend prüft fällige Aufgaben und sendet rechtzeitig (z.B. 15, 30 oder 60 Min. vor Fälligkeit) formatierte Discord-Embeds via Webhook.
|
||
- Vollständiger JSON-DB-Export auf Knopfdruck.
|
||
- [x] **Cross-Browser & Performance Fixes (Chrome & Firefox):**
|
||
- SQLite Connection-Pool Deadlock durch striktes Schließen von `sql.Rows` vor Unterabfragen behoben.
|
||
- Stale Token Auto-Resolution via `EnsureDefaultUser()`.
|
||
- `Cache-Control: no-store` auf allen API-Routen und `index.html`.
|
||
- Optimistisches Leeren des Capture-Inputs und 8-Sekunden-Fetch-Timeout.
|
||
|
||
---
|
||
|
||
## 🎨 1. Design-System & CSS-Architektur (`frontend/src/index.css`)
|
||
|
||
Alle Farben, Abstände und Radien werden zentral in [`frontend/src/index.css`](file:///home/sandro/Dokumente/git/MindSift/frontend/src/index.css) über CSS-Variablen gesteuert:
|
||
|
||
```css
|
||
:root {
|
||
/* Monochromer Hintergrund (Dunkles Schiefer/Anthrazit) */
|
||
--bg-app: #121316;
|
||
--bg-panel: #1a1c20;
|
||
--bg-panel-elevated: #22252a;
|
||
--bg-panel-hover: #282c33;
|
||
|
||
/* Einzige Akzentfarbe + hellere/gedimmte Variante */
|
||
--accent-primary: #2563eb;
|
||
--accent-hover: #3b82f6;
|
||
--accent-dim: rgba(37, 99, 235, 0.14);
|
||
--accent-border: rgba(59, 130, 246, 0.45);
|
||
|
||
/* Kanten & Linien */
|
||
--border-color: rgba(255, 255, 255, 0.09);
|
||
--border-subtle: rgba(255, 255, 255, 0.05);
|
||
|
||
/* Eckiges Layout (0px für Karten/Panels, minimal für Inputs) */
|
||
--radius-card: 0px;
|
||
--radius-btn: 2px;
|
||
--radius-input: 2px;
|
||
}
|
||
```
|
||
|
||
### Zentrale Komponenten-Klassen
|
||
- `.panel-surface` / `.panel-elevated`: Eckige Container mit `1px solid var(--border-color)`.
|
||
- `.btn-action`: Monochromer, kompakter Button (`border-radius: var(--radius-btn)`).
|
||
- `.btn-primary`: Akzent-Button auf `--accent-primary`.
|
||
- `.input-field`: Minimal gerundetes (`2px`) Eingabefeld für Eigenschaften/Optionen.
|
||
- `.tab-btn` / `.tab-btn-active`: Eckige, monochrome Header-Tabs.
|
||
- `.badge-accent`: Kompaktes Akzent-Badge für Zähler und Tags.
|
||
- **Ausnahme für Rundungen (`rounded-full`):** Nur die Schnell-Eingabe-Blase (`InstantCaptureBar`) im Header, der Profil-Avatar und Toggle-Switches sind rund.
|
||
|
||
---
|
||
|
||
## 🗄️ 2. Datenbank-Architektur (SQLite Schema & Tabellen)
|
||
|
||
MindSift speichert alle Daten lokal und hochperformant in SQLite (`/data/mindsift.db`).
|
||
|
||
### Tabellen-Übersicht
|
||
|
||
1. **`users`**
|
||
- `id TEXT PRIMARY KEY`, `email TEXT UNIQUE`, `password_hash TEXT`, `display_name TEXT`
|
||
- `role TEXT DEFAULT 'member'` (`'admin'` | `'member'`)
|
||
- `is_active INTEGER DEFAULT 1`
|
||
- `discord_webhook_url TEXT DEFAULT ''`
|
||
- `notify_before_minutes INTEGER DEFAULT 15`
|
||
- `created_at DATETIME`, `updated_at DATETIME`
|
||
|
||
2. **`thoughts` (Quick Dumps, Triage & Aufgaben)**
|
||
- `id TEXT PRIMARY KEY`, `user_id TEXT`, `content TEXT`, `source TEXT`, `priority TEXT`
|
||
- `status TEXT` (`'inbox'` | `'converted'` | `'discarded'`)
|
||
- `converted_type TEXT` (`'task'` | `'document'` | `'list_item'`)
|
||
- `converted_ref_id TEXT` (Referenz auf Document oder ListItem)
|
||
- `task_list_id TEXT` (FK auf `task_lists.id`)
|
||
- `kanban_status TEXT DEFAULT 'todo'` (`'todo'` | `'in_progress'` | `'done'`)
|
||
- `notes TEXT DEFAULT ''` (zusätzliche Aufgabenbeschreibung/Notizen)
|
||
- `due_date DATETIME` (Fälligkeitsdatum)
|
||
- `alarm_sent INTEGER DEFAULT 0` (Flag für Discord-Benachrichtigung)
|
||
- `created_at DATETIME`, `updated_at DATETIME`
|
||
|
||
3. **`task_lists` (Mehrere Aufgabenlisten)**
|
||
- `id TEXT PRIMARY KEY`, `user_id TEXT`, `space_id TEXT` (optional für Team-Sharing)
|
||
- `name TEXT NOT NULL`, `color TEXT DEFAULT '#2563eb'`, `icon TEXT DEFAULT 'list'`
|
||
- `is_default INTEGER DEFAULT 0`, `created_at DATETIME`, `updated_at DATETIME`
|
||
|
||
4. **`organizations` & `teams` & `team_members`**
|
||
- `organizations`: `id TEXT PRIMARY KEY`, `name TEXT`, `owner_id TEXT`, `created_at DATETIME`, `updated_at DATETIME`
|
||
- `teams`: `id TEXT PRIMARY KEY`, `organization_id TEXT`, `name TEXT`, `created_at DATETIME`
|
||
- `team_members`: `team_id TEXT`, `user_id TEXT`, `role TEXT DEFAULT 'member'`, `joined_at DATETIME`, `PRIMARY KEY (team_id, user_id)`
|
||
|
||
5. **`system_settings` (Admin-Konfiguration)**
|
||
- `key TEXT PRIMARY KEY`, `value TEXT`, `updated_at DATETIME`
|
||
- Wichtige Schlüssel:
|
||
- `sharing_isolation_mode`: `'open'` | `'teams_only'` | `'strict_private'`
|
||
- `allow_public_registration`: `'true'` | `'false'`
|
||
|
||
6. **`documents` & `document_blocks` (Workspace & Disk Sync)**
|
||
- `documents`: `id TEXT PRIMARY KEY`, `user_id TEXT`, `space_id TEXT`, `title TEXT`, `folder_path TEXT DEFAULT '/'`, `is_archived INTEGER`, `created_at DATETIME`, `updated_at DATETIME`
|
||
- `document_blocks`: `id TEXT PRIMARY KEY`, `document_id TEXT`, `block_type TEXT` (`paragraph`, `heading1`, `heading2`, `heading3`, `todo`, `bullet`, `callout`, `code`, `table`, `progress`), `content TEXT`, `order_index INTEGER`, `metadata TEXT`, `updated_at DATETIME`
|
||
|
||
7. **`spaces`, `space_members`, `shared_lists`, `list_items`, `api_keys`**
|
||
- Verwaltung geteilter Einkaufs-/Checklisten und privater Ingestion-Keys.
|
||
|
||
---
|
||
|
||
## ⚙️ 3. State-Machine: Quick Dump, Direkt-Zuordnung & Triage
|
||
|
||
```
|
||
[ HEADER QUICK-CAPTURE BAR ]
|
||
│
|
||
┌───────────────────────┼───────────────────────┐
|
||
│ Standard (Enter) │ Direkt-Option gewählt │
|
||
▼ ▼ ▼
|
||
status: 'inbox' status: 'converted' status: 'converted'
|
||
(Quick Dump Deck) ├── target: 'task' ├── target: 'list_item'
|
||
│ └── target: 'document' └── Broadcast via WS
|
||
│
|
||
├──► Swipe Links / "Wegwerfen" (H / ←) ──► status: 'discarded'
|
||
├──► "Später" (Leertaste) ──► ans Ende der lokalen Queue
|
||
├──► Swipe Rechts / "Als Aufgabe" (L/→)──► status: 'converted' (type: 'task')
|
||
├──► "Mit Termin" (T) ──► status: 'converted' (type: 'task', dueDate)
|
||
├──► "Als Notiz" (D) ──► status: 'converted' (type: 'document' + Doc angelegt)
|
||
└──► "Auf Liste" ──► status: 'converted' (type: 'list_item' + Item angelegt)
|
||
```
|
||
|
||
### API-Endpunkt für Capture (`POST /api/v1/inbox`)
|
||
Akzeptiert:
|
||
- `content`: Fließtext des Gedankens
|
||
- `priority`: `'low'` | `'medium'` | `'high'`
|
||
- `target`: `'inbox'` (Standard) | `'task'` | `'list_item'` | `'document'`
|
||
- `taskListId`: ID der Aufgabenliste (wenn `target === 'task'`)
|
||
- `targetListId`: ID der Zielliste (wenn `target === 'list_item'`)
|
||
- `dueDate`: Optionales ISO-Datum
|
||
|
||
---
|
||
|
||
## 🔔 4. Discord-Alarm-Scheduler & Webhook-Architektur
|
||
|
||
1. **Hintergrund-Ticker im Go-Backend (`internal/api/server.go`):**
|
||
- Ein Hintergrund-Goroutine-Ticker läuft alle 60 Sekunden.
|
||
- Fragt `GetDueTasksForAlarm()` ab: Sucht nach offenen Aufgaben (`status = 'converted'`, `converted_type = 'task'`, `kanban_status != 'done'`), deren `due_date` in der Vorwarnzeit liegt (`due_date <= now + notifyBeforeMinutes`) und bei denen `alarm_sent == 0` ist.
|
||
- Sendet ein JSON-Embed an die in den Benutzer-Einstellungen hinterlegte `discord_webhook_url`:
|
||
```json
|
||
{
|
||
"username": "MindSift Reminder",
|
||
"embeds": [{
|
||
"title": "⏰ Aufgabe fällig: <Titel>",
|
||
"description": "<Notizen>",
|
||
"color": 2450411,
|
||
"fields": [
|
||
{"name": "Fällig am", "value": "<Datum>", "inline": true},
|
||
{"name": "Priorität", "value": "<Prio>", "inline": true}
|
||
]
|
||
}]
|
||
}
|
||
```
|
||
- Markiert den Gedanken in SQLite mit `MarkTaskAlarmSent(taskID)` als benachrichtigt.
|
||
|
||
2. **Eingehende Webhooks / Private API-Tokens:**
|
||
- Erreichbar unter `POST /api/v1/inbox` mit Header `X-API-Key: ms_...`.
|
||
- Ermöglicht direkte Einbindung in Shell-Skripte, Crons oder Drittsysteme via `curl`.
|
||
|
||
---
|
||
|
||
## 📝 5. Workspace: Word/Notion-Toolbar, Wiki-Links & Disk-Sync
|
||
|
||
1. **Formatierungs-Werkzeuge:**
|
||
- Markdown-Syntax wird in Echtzeit formatiert und visuell aufbereitet.
|
||
- Buttons für H1, H2, H3, Fett (`**`), Kursiv (`*`), Durchgestrichen (`~~`), Code-Blöcke (```` ``` ````), Aufzählungen (`- `), Todos (`- [ ] `), Callouts (`> `), Markdown-Tabellen (`| Spalte 1 | Spalte 2 |`) und Fortschrittsbalken (`[progress:65%]`).
|
||
2. **Wiki-Links (`[[Dokumenttitel]]`):**
|
||
- Interne Querverweise werden als interaktive Links gerendert.
|
||
- Ein Klick auf `[[Dokumenttitel]]` durchsucht die Workspace-Dokumente und öffnet das verlinkte Dokument direkt im Editor.
|
||
3. **Physische Speicherung auf Festplatte:**
|
||
- Jede Notiz wird zusätzlich zur SQLite-Tabelle als reine `.md`-Datei unter `/data/workspace/<user_id>/<folder_path>/<slug>.md` gespeichert.
|
||
- Ermöglicht jederzeitige manuelle Datensicherung, Git-Versionierung oder Backup-Extraktion ohne Datenbank-Abhängigkeit.
|
||
|
||
---
|
||
|
||
## 🛡️ 6. Kritische Backend-Regeln (SQLite & HTTP)
|
||
|
||
1. **NIEMALS verschachtelte Queries bei offenem `sql.Rows`-Cursor ausführen:**
|
||
- In [`internal/storage/sqlite.go`](file:///home/sandro/Dokumente/git/MindSift/internal/storage/sqlite.go) muss jede Schleife `for rows.Next()` zuerst alle Zeilen in ein Slice einlesen und sofort `_ = rows.Close()` aufrufen, **bevor** Folgeabfragen (`GetListItems`, `GetSpaceMembers`, `QueryRow` etc.) ausgeführt werden.
|
||
2. **SQLite DSN-Pragmas:**
|
||
- Jede Verbindung im `sql.DB`-Pool erhält über den DSN automatisch:
|
||
`?_pragma=busy_timeout(10000)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=foreign_keys(1)`
|
||
- `MaxOpenConns(8)` und `MaxIdleConns(4)` konfiguriert.
|
||
3. **Thread-Safe Default User (`EnsureDefaultUser`):**
|
||
- Durch `s.initMu.Lock()` abgesichert, damit parallele Browser-Requests beim ersten Laden niemals in einen `UNIQUE constraint`-Konflikt laufen.
|
||
4. **Kein Browser-Caching auf API & `index.html`:**
|
||
- `writeJSON` und der SPA-Fallback setzen `Cache-Control: no-store, no-cache, must-revalidate`, damit Chrome und Firefox nach jedem Container-Update sofort den aktuellen Stand zeigen.
|
||
|
||
---
|
||
|
||
## 📡 7. WebSocket Realtime Protocol (Geteilte Listen)
|
||
|
||
Verbindungs-URL: `ws(s)://<host>/api/v1/ws/spaces/:spaceId?token=<jwt>`
|
||
|
||
### Events
|
||
- `ITEM_CREATED` (auch ausgelöst, wenn ein Gedanke per Direkt-Zuordnung oder Triage auf eine Liste geschoben wird)
|
||
- `ITEM_TOGGLED`
|
||
- `ITEM_DELETED`
|
||
- `LIST_CREATED` / `LIST_DELETED` / `LIST_CLEARED`
|
||
- `USER_PRESENCE`
|
||
|
||
---
|
||
|
||
## ⌨️ 8. Tastatur-Shortcuts (Desktop)
|
||
|
||
| Taste | Kontext | Aktion |
|
||
| :--- | :--- | :--- |
|
||
| `C` oder `/` | Überall | Fokus sofort oben in die Quick-Dump-Eingabeleiste |
|
||
| `Enter` | Capture-Leiste | Sofort speichern (Quick Dump oder gewähltes Direkt-Ziel) |
|
||
| `Esc` | Capture-Leiste | Eingabe leeren & Dropdown schließen |
|
||
| `H` oder `←` | Triage-Deck | Aktiven Quick Dump wegwerfen (`discard`) |
|
||
| `L` oder `→` | Triage-Deck | Aktiven Quick Dump sofort zur Aufgabe machen (`task`) |
|
||
| `T` | Triage-Deck | Datums-Auswahl für Termin-Aufgabe öffnen |
|
||
| `D` | Triage-Deck | Aktiven Quick Dump sofort in Workspace-Notiz umwandeln |
|
||
| `Leertaste` | Triage-Deck | Karte überspringen („Später" – ans Ende des Stapels schieben) |
|
||
|
||
---
|
||
|
||
## 🚀 9. Gitea CI/CD & Docker Workflow
|
||
|
||
- **Arbeits-Branch:** `dev` (`origin/dev`)
|
||
- **Ziel-Image:** `git.dsit.me/sandrodach/mindsift:dev`
|
||
- **Build-Prinzip:** Ein einziger Multi-Stage-Build ([`Dockerfile`](file:///home/sandro/Dokumente/git/MindSift/Dockerfile)), der Frontend-Typecheck, Vite-Build und Go-Unit-Tests (`go test -v ./...` inkl. [`internal/api/server_test.go`](file:///home/sandro/Dokumente/git/MindSift/internal/api/server_test.go)) direkt beim Image-Bau ausführt und bei Erfolg sofort pusht.
|