Files
MindSift/Doku/database.md
T
sandrodachandClaude Opus 5.5 1ea0c78b41
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 50s
feat(notes): operation log, block leases and sync API for collaborative editing
Phase 1 of the collaboration plan (server side):
- internal/collab: block tree with parent IDs and fractional order keys,
  operations with conflict rules (version check, block leases, conflict
  copies instead of lost text, restore on late edits, cycle checks),
  in-memory leases/sessions and long-poll notifier.
- Storage: per-note lock + transaction, append-only effect log (doc_ops),
  idempotent results per opId (doc_op_results), snapshot, log paging,
  central DocRole. Old flat block data is converted to the tree.
- The existing whole-document save (PUT/PATCH) runs as an adapter through
  the same engine, so the current editor keeps working and writes to the log.
- API: GET /docs/{id}/snapshot, POST /docs/{id}/sync, GET /docs/{id}/changes
  (long poll), POST /docs/{id}/leave; DOC_SEQ poke for WebSocket/SSE rooms.
- Backend accepts editor (write) shares for notes and folders; the forced
  read-only migration is gone. The share dialog still offers read only.
- Tests: conflict matrix, order keys, leases, storage adapter round trip,
  idempotency, API protocol incl. long poll, and a randomized simulation
  (4 clients, lost/duplicated requests and responses, offline phases,
  lease expiry) checking convergence, no lost text and exactly-once results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:47:56 +01:00

6.5 KiB

Datenbank

SQLite-Datei mindsift.db (Standard: $DATA_DIR/mindsift.db), geöffnet mit journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON, busy_timeout=10000 und höchstens 8 Verbindungen. Das Schema entsteht in Store.migrate() (sqlite.go): zuerst CREATE TABLE IF NOT EXISTS, danach eine Liste idempotenter ALTER TABLE/CREATE-Anweisungen für ältere Datenbanken. IDs sind Text mit Präfix (usr_…, th_…, tkl_…, lst_…, tls_…, blk_…, doc_…).

Die Datei migrations/001_init.sql wird vom Code nicht gelesen und ist veraltet (FLAW-13).

Tabellen

Benutzer und Zugang

Tabelle Zweck Wichtige Spalten
users Konten email (eindeutig), username, password_hash, display_name, role (admin/member), is_active, token_version, alarm_channels (kommagetrennt), alarm_email, discord_webhook_url, custom_webhook_url, notify_before_minutes, avatar_url
user_invites Einladungscodes code, email, role, team_id, expires_at, used_by
revoked_tokens Sperrliste ausgeloggter JWTs token_hash, expires_at
api_keys Webhook-/API-Schlüssel key_prefix, key_hash (SHA-256), scopes (nicht ausgewertet), last_used_at
system_settings Schlüssel/Wert für Instanz-Einstellungen sharing_mode, allow_registration, instance_name, smtp_*

Gedanken und Aufgaben

Tabelle Zweck Wichtige Spalten
thoughts Gedanken und Aufgaben in einer Tabelle status (inbox/kept/converted/discarded), converted_type (task/document/list_item/document_block), converted_ref_id, priority, is_completed, due_date (nur Datum), task_list_id, kanban_status, notes, subtasks (JSON), tags (JSON), alarm_sent, sort_order, source (manual, voice, webhook …)
task_lists Aufgabenlisten user_id (Eigentümer), team_id (Altfeld), title, icon, color, columns (JSON, Kanban-Spalten)
task_list_shares Freigaben von Aufgabenlisten target_type (team/user), target_id, permission (editor/viewer, Standard editor)

Eine Aufgabe ist ein thought mit status='kept' oder status='converted' AND converted_type='task'. Aufgaben, die aus einem Notiz-Block entstehen, tragen converted_type='document_block' und converted_ref_id='<docId>:<blockId>'.

Workspace

Tabelle Zweck
documents Notizen: title, icon, folder_path, tags (JSON), is_archived, markdown_cache, shared_folder_id, sub_folder_path, Altspalten content, blocks_json
blocks Outliner-Blöcke als Baum: document_id, parent_id ('' = oberste Ebene), order_key (Sortierschlüssel unter Geschwistern), type, content, checked, collapsed, lang, version (Textversion), conflict_of/conflict_author (Konfliktkopien), updated_by, deleted (Grabstein). level/position sind Altspalten und werden nicht mehr gepflegt; Ebene und Reihenfolge berechnet der Server beim Lesen aus dem Baum.
doc_ops Änderungs-Log je Notiz: seq (fortlaufend pro Notiz), op_id, user_id, client_id, kind, block_id, payload (Effekt als JSON). Abspielen aller Einträge ergibt den Stand.
doc_op_results Ergebnis je Client-Operation (op_id eindeutig) für idempotente Wiederholungen
block_tags #Hashtags je Block (tag, document_id, block_id)
tag_pages Freitext-Notiz je Benutzer und Tag
doc_pins Favoriten je Benutzer
doc_shares, doc_team_shares Notiz-Freigaben an Benutzer bzw. Gruppen (permission: read = Leser, write = Bearbeiter)
workspace_folders Explizit angelegte Ordnerpfade
workspace_shared_folders Geteilte Ordner (Entität mit Eigentümer)
workspace_folder_shares Freigaben geteilter Ordner
workspace_folder_mounts Persönlicher Einhängepfad je Empfänger und geteiltem Ordner
doc_user_folders Persönliche Ablage einer geteilten Einzelnotiz je Empfänger

Blocktypen: h1, h2, h3, paragraph, text, todo, bullet, code, callout, divider, table, progress.

Listen

Tabelle Zweck
lists Geteilte Listen: title, icon, list_type (shopping/checklist/vacation)
list_shares Freigaben: target_type, target_id, role (editor/viewer)
list_items Einträge: content, category, quantity, is_completed, completed_by(_name), sort_order

Gruppen

Tabelle Zweck
teams name, description, org_id (Standard default)
team_members team_id, user_id, role (admin/member)
organizations Rudimentär; wird vom UI nicht mehr benötigt (FLAW-24)

Beziehungen

users 1─n thoughts            thoughts n─1 task_lists (task_list_id, lose)
users 1─n task_lists          task_lists 1─n task_list_shares ─(team|user)→ teams/users
users 1─n documents 1─n blocks 1─n block_tags
documents n─n users           via doc_shares / doc_team_shares (nur Lesen)
workspace_shared_folders 1─n workspace_folder_shares / workspace_folder_mounts
users 1─n lists 1─n list_items;  lists 1─n list_shares
teams 1─n team_members n─1 users

thoughts.task_list_id ist keine Fremdschlüsselbeziehung; beim Löschen einer Liste setzt der Code die Verweise auf ''.

Migrationen und Aufräumarbeiten beim Start

  • Spalten/Tabellen/Indizes werden nachgezogen (ALTER TABLE … ADD COLUMN, Fehler werden ignoriert).
  • Veraltete Tabellen werden gelöscht: spaces, space_members, space_team_shares, document_crdt_state, document_crdt_updates, document_blocks, doc_block_tags.
  • users.username wird aus dem E-Mail-Präfix aufgefüllt.
  • Notiz-Synchronisation (migrateDocSync): neue Spalten an blocks und documents (head_seq, title_version), Tabellen doc_ops und doc_op_results. Blöcke im alten flachen Format (nur level/position) werden einmalig in den Baum umgewandelt.
  • Standardwerte in system_settings (INSERT OR IGNORE): sharing_mode=teams_only, allow_registration=false, instance_name=MindSift, SMTP leer.
  • Die Tabelle teams wird einmalig ohne Fremdschlüssel auf organizations neu aufgebaut.

Sicherung

Die gesamte Instanz besteht aus der Datei mindsift.db (plus -wal/-shm im laufenden Betrieb). Konsistent sichern per sqlite3 mindsift.db ".backup 'backup.db'" oder durch Stoppen des Containers und Kopieren des data/-Verzeichnisses. Benutzerexport: GET /api/v1/export (JSON mit Gedanken, Notizen, Listen, Aufgabenlisten).