docs: rewrite documentation and README from current codebase, add flaw analysis
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 8s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 8s
Remove outdated claims (CRDT, DocRooms, presence, .md files on disk) and document the actual architecture, API, permissions and known flaws. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4e9c4ffa8c
commit
bc13235da6
14 files changed
+733
-1618
No files matched your search
+73
-128
@@ -1,142 +1,87 @@
|
||||
# 🏛️ Systemarchitektur & Technologie-Stack
|
||||
# Architektur
|
||||
|
||||
Dieses Dokument beschreibt die interne Architektur von MindSift, die Interaktion zwischen den Systemkomponenten, das Concurrency- und Thread-Safety-Modell sowie die Deployment-Strategie.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 1. Schichten-Architektur
|
||||
|
||||
MindSift ist als monolithische, hochperformante Single-Binary-Lösung konzipiert. Das Go-Backend liefert sowohl die REST- und WebSocket-Schnittstellen aus als auch die vorkompilierte React Single Page Application (SPA) als eingebettetes statisches Dateisystem (`embed.FS`).
|
||||
## Überblick
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Frontend (React 18) │
|
||||
│ Vite • TypeScript • Tailwind CSS • Lucide • Motion │
|
||||
└───────────────────────────┬────────────────────────────┘
|
||||
│ HTTPS REST / WSS WebSocket
|
||||
┌───────────────────────────▼────────────────────────────┐
|
||||
│ Go Chi Routing Layer │
|
||||
│ CORS • RealIP • Recoverer • Auth / Role Guard │
|
||||
├───────────────────────────┬────────────────────────────┤
|
||||
│ Business Engine │ Gorilla WebSocket │
|
||||
│ Triage • Tasks • Alarms │ Rooms • Hub • Doc-Locking │
|
||||
├───────────────────────────┴────────────────────────────┤
|
||||
│ Storage Abstraction │
|
||||
│ sqlite.go (modernc.org/sqlite pure Go, WAL-Mode) │
|
||||
└───────────────────────────┬────────────────────────────┘
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
▼ ▼
|
||||
/data/mindsift.db /data/workspace/
|
||||
(SQLite Datenbank) (Physische .md Dateien)
|
||||
Browser (React SPA, PWA-Manifest)
|
||||
│ REST /api/v1/* Authorization: Bearer <JWT | mindsift_sec_…>
|
||||
│ WebSocket /api/v1/ws Fallback: SSE /api/v1/events
|
||||
▼
|
||||
Go-Server (chi) ── internal/api Handler, Middleware, Alarm-Ticker
|
||||
│ internal/ws Hub (Räume, Benutzer-Kanäle, SSE)
|
||||
│ internal/auth JWT, bcrypt, API-Key-Hash
|
||||
▼ internal/config
|
||||
SQLite (modernc.org/sqlite, WAL) ── internal/storage
|
||||
```
|
||||
|
||||
---
|
||||
Das Frontend liegt per `go:embed` im Binary ([cmd/server/main.go](../cmd/server/main.go)). Unbekannte Pfade liefern `index.html` (SPA-Routing); `index.html`, `sw.js` und `manifest.json` werden mit `no-store` ausgeliefert.
|
||||
|
||||
## 🛠️ 2. Technologie-Stack
|
||||
## Stack
|
||||
|
||||
### Backend (Go 1.23)
|
||||
- **HTTP Routing:** [`github.com/go-chi/chi/v5`](https://github.com/go-chi/chi) – Minimalistischer, idiomatisch schneller Router mit Middleware-Ketten.
|
||||
- **WebSocket Hub:** [`github.com/gorilla/websocket`](https://github.com/gorilla/websocket) – Verbindungs-Upgrade, Keep-Alive/Ping-Pong und Room-Multiplexing.
|
||||
- **Datenbank-Treiber:** [`modernc.org/sqlite`](https://gitlab.com/cznic/sqlite) – CGO-freier, rein in Go übersetzter SQLite-Treiber für einfache plattformübergreifende Kompilierung und Containerisierung ohne GCC-Toolchain.
|
||||
- **Authentifizierung:** [`github.com/golang-jwt/jwt/v5`](https://github.com/golang-jwt/jwt) (HMAC-SHA256 Token) und [`golang.org/x/crypto/bcrypt`](https://pkg.go.dev/golang.org/x/crypto/bcrypt) für Passwort-Hashing.
|
||||
| Schicht | Technik |
|
||||
|---|---|
|
||||
| Backend | Go 1.23, `go-chi/chi` v5, `go-chi/cors`, `gorilla/websocket`, `golang-jwt/jwt` v5, `x/crypto/bcrypt` |
|
||||
| Datenbank | SQLite über `modernc.org/sqlite` (reines Go, kein CGO), WAL, `foreign_keys=ON` |
|
||||
| Frontend | React 18, TypeScript, Vite 5, Tailwind 3, framer-motion (Triage-Karten), lucide-react, prismjs (Code-Blöcke) |
|
||||
| Auslieferung | Mehrstufiges Dockerfile (Node 22 → Go 1.23 → Alpine 3.21), Image `git.dsit.me/sandrodach/mindsift` |
|
||||
|
||||
### Frontend (React 18 SPA)
|
||||
- **Build-Tool:** [Vite 5](https://vitejs.dev/) für schnelles Hot-Module-Replacement (HMR) und optimierte Production-Bundles.
|
||||
- **Sprache:** TypeScript 5 mit strikter Typisierung.
|
||||
- **Design & Layout:** [Tailwind CSS 3](https://tailwindcss.com/) in Verbindung mit dem MindSift Single-File CSS Theming System.
|
||||
- **Icons & Animationen:** [Lucide React](https://lucide.dev/) und [Framer Motion](https://www.framer.com/motion/) für Gesten, Karten-Swipes und Übergänge.
|
||||
## Verzeichnisse
|
||||
|
||||
---
|
||||
| Pfad | Inhalt |
|
||||
|---|---|
|
||||
| `cmd/server/` | `main.go`, eingebettetes `dist/` |
|
||||
| `internal/api/` | `server.go` (Routen, Handler, Alarm-Ticker, SMTP/Discord/Webhook-Versand) |
|
||||
| `internal/storage/` | `sqlite.go` (Schema, Migrationen, gesamte Datenlogik) |
|
||||
| `internal/domain/` | Strukturen (`models.go`), ID-Erzeugung, Tag-Extraktion |
|
||||
| `internal/ws/` | `hub.go` |
|
||||
| `internal/auth/` | Tokens und Passwörter |
|
||||
| `internal/config/` | Umgebungsvariablen |
|
||||
| `frontend/src/features/` | `triage`, `workspace`, `lists`, `teams`, `settings`, `capture`, `auth`, `help` |
|
||||
| `frontend/src/lib/` | `api.ts` (REST-Client, Transport-Umschaltung WS/SSE), `types.ts`, `markdown.tsx`, `theme.ts` |
|
||||
| `frontend/src/themes/` | CSS pro Theme |
|
||||
|
||||
## 📁 3. Verzeichnisstruktur des Projekts
|
||||
## Start und Laufzeit
|
||||
|
||||
1. `config.Load()` liest die Umgebung (siehe unten).
|
||||
2. `storage.NewStore()` öffnet SQLite (`busy_timeout`, WAL, max. 8 Verbindungen) und führt `migrate()` aus.
|
||||
3. SMTP-Werte aus der Umgebung werden in `system_settings` übernommen, sofern dort noch nichts steht.
|
||||
4. Mit Demo-Flag legt `EnsureDemoAccounts` die Konten `admin/admin` und `user/user` an.
|
||||
5. `api.NewServer` startet den **Alarm-Ticker** (jede Minute) und baut den Router. Server-Timeouts: Lesen 15 s, Schreiben 30 s, Leerlauf 60 s. Beim Beenden wird sauber heruntergefahren.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
| Variable | Standard | Bedeutung |
|
||||
|---|---|---|
|
||||
| `PORT` | `8080` | HTTP-Port |
|
||||
| `DATA_DIR` | `./data` (Container: `/data`) | Verzeichnis der Datenbank |
|
||||
| `DATABASE_URL` | leer | `sqlite:///pfad/datei.db` oder Pfad; ohne Angabe `DATA_DIR/mindsift.db`. Postgres-URLs werden **nicht** unterstützt (siehe Hinweis) |
|
||||
| `JWT_SECRET` | festes Fallback (unsicher!) | Signaturschlüssel der Tokens; unbedingt setzen, siehe [FLAW-04](flaws.md) |
|
||||
| `BASE_URL` | `http://localhost:<PORT>` | Basis-URL der Instanz |
|
||||
| `ALLOW_REGISTRATION` | `false` | Startwert; die Einstellung im Admin-UI (`system_settings`) hat Vorrang |
|
||||
| `LOG_LEVEL` / `DEBUG` | `info` / `false` | `debug`, `info`, `warn`, `error`; `DEBUG=true` erzwingt `debug` |
|
||||
| `MINDA_DEMO` (auch `MINDSIFT_DEMO`, `DEMO_MODE`) | `false` | Demo-Konten anlegen |
|
||||
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`, `SMTP_TLS` | leer / `587` / `true` | Mailversand, auch im Admin-UI einstellbar |
|
||||
|
||||
Hinweis: `DATABASE_URL` mit `postgres…` führt dazu, dass der Standardpfad in `DATA_DIR` genutzt wird; Postgres ist nicht implementiert.
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
- **Login** mit E-Mail, Benutzername (aus dem E-Mail-Präfix) oder `login`. Passwörter: bcrypt (`DefaultCost`).
|
||||
- **JWT:** HS256, Laufzeit 30 Tage, Claims `userId`, `email`, `displayName`, `tokenVersion`, `jti`. Gültig nur, wenn Token nicht in `revoked_tokens` steht, das Konto aktiv ist und `tokenVersion` mit `users.token_version` übereinstimmt. `token_version` wird bei Passwortänderung, Rollenwechsel, Deaktivierung und „Alle Sitzungen beenden“ erhöht.
|
||||
- **Logout** schreibt den Token-Hash in `revoked_tokens`.
|
||||
- **API-Keys:** `mindsift_sec_<hex>`, gespeichert als SHA-256, nur das Präfix bleibt sichtbar. Der Key gilt als Bearer-Token oder in `X-API-Key` und authentifiziert als Besitzer ([FLAW-07](flaws.md)).
|
||||
- **Registrierung:** nur mit Einladungscode oder wenn `allow_registration` aktiv ist. Das erste Konto entsteht über `/auth/setup` und wird Administrator.
|
||||
|
||||
## Hintergrundjob: Alarme
|
||||
|
||||
`startAlarmTicker` prüft jede Minute alle offenen, noch nicht gemeldeten Aufgaben mit Fälligkeit. Ist `due_date <= heute` (Serverzeit), sendet er über die Kanäle des Benutzers (`email`, `discord`, `webhook`) und setzt `alarm_sent`, sobald mindestens ein Kanal erfolgreich war. Details und Einschränkungen: [modules/tasks.md](modules/tasks.md), [FLAW-12](flaws.md).
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
MindSift/
|
||||
├── cmd/
|
||||
│ └── server/
|
||||
│ └── main.go # Einstiegspunkt: Initialisierung, Config, Store, Start
|
||||
├── internal/
|
||||
│ ├── api/
|
||||
│ │ ├── server.go # HTTP-Routen, Handler-Methoden, Middleware
|
||||
│ │ └── server_test.go # Integrationstests für alle REST- und Ingest-Routen
|
||||
│ ├── auth/
|
||||
│ │ ├── auth.go # JWT Validierung, Token-Erstellung, Context-Helpers
|
||||
│ │ └── apikey.go # API-Key Erzeugung (mindsift_sec_...) & SHA-256 Hashing
|
||||
│ ├── config/
|
||||
│ │ └── config.go # Environment-Variablen (PORT, DATA_DIR, JWT_SECRET, SMTP)
|
||||
│ ├── domain/
|
||||
│ │ └── models.go # Go-Structs für Thought, Document, List, User, Team etc.
|
||||
│ ├── storage/
|
||||
│ │ ├── sqlite.go # DB-Schema, CRUD-Operationen, Queries, Migrationen
|
||||
│ │ └── storage_test.go # Unit-Tests für Speicheroperationen
|
||||
│ └── ws/
|
||||
│ └── hub.go # WebSocket Client, Room-Management, Locking-Mechanismus
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # Wiederverwendbare UI-Elemente (Modals, Avatar, Menüs)
|
||||
│ │ ├── context/ # MobileActionContext (Mobile Bottom Bar State)
|
||||
│ │ ├── features/
|
||||
│ │ │ ├── auth/ # SetupWizard & Login
|
||||
│ │ │ ├── capture/ # InstantCaptureBar (Schnelleingabe)
|
||||
│ │ │ ├── help/ # Integrierte Benutzerdokumentation
|
||||
│ │ │ ├── lists/ # Geteilte Listen & Einkauf
|
||||
│ │ │ ├── settings/ # Benutzer-, Admin- & Theme-Einstellungen
|
||||
│ │ │ ├── teams/ # Gruppen & Mitglieder-Management
|
||||
│ │ │ ├── triage/ # Triage-Deck, Aufgabenliste, Kanban & TaskModal
|
||||
│ │ │ └── workspace/ # Outliner-Editor, Datei-Explorer, Wiki & Rollup
|
||||
│ │ ├── lib/ # api.ts (HTTP/WS Client), types.ts, theme.ts
|
||||
│ │ ├── themes/ # Eigenständige Theme CSS-Dateien (dark, light, etc.)
|
||||
│ │ ├── App.tsx # Root-Komponente, Navigation, Top/Bottom Bars
|
||||
│ │ └── main.tsx # React DOM Bootstrap
|
||||
│ ├── package.json
|
||||
│ └── vite.config.ts
|
||||
├── Doku/ # Dieses technische Wiki
|
||||
├── Dockerfile # Multi-Stage Build Definition
|
||||
└── compose.yaml # Docker Compose Vorlage für Produktivbetrieb
|
||||
Stage 1 node:22-alpine npm ci && npm run build → frontend/dist
|
||||
Stage 2 golang:1.23 go vet, go test, go build → /app/mindsift (dist wird eingebettet)
|
||||
Stage 3 alpine:3.21 nur Binary, ca-certificates, tzdata
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 4. Thread-Safety & Concurrency-Modell
|
||||
|
||||
Da SQLite Datei-basiert arbeitet und Go standardmäßig hochgradig nebenläufig (Multi-Threaded via Goroutines) operiert, sind folgende Schutzmaßnahmen implementiert:
|
||||
|
||||
### 1. SQLite Pragmas & WAL-Modus
|
||||
Im Storage-Layer ([`internal/storage/sqlite.go`](../internal/storage/sqlite.go)) wird die Datenbankverbindung beim Start zwingend mit folgenden DSN-Pragmas konfiguriert:
|
||||
```go
|
||||
dsn := filepath.Join(dataDir, "mindsift.db") +
|
||||
"?_pragma=busy_timeout(10000)&" +
|
||||
"_pragma=journal_mode(WAL)&" +
|
||||
"_pragma=synchronous(NORMAL)&" +
|
||||
"_pragma=foreign_keys(1)"
|
||||
```
|
||||
- **`journal_mode(WAL)`:** Write-Ahead Logging erlaubt parallele Leser (`SELECT`), während ein Schreiber (`INSERT`/`UPDATE`) aktiv ist. Leser blockieren niemals Schreiber und Schreiber blockieren niemals Leser.
|
||||
- **`busy_timeout(10000)`:** Sollte ein Schreibvorgang kurzzeitig blockiert sein, wartet SQLite bis zu 10.000 ms (10 Sekunden), bevor ein `SQLITE_BUSY` Fehler geworfen wird.
|
||||
- **`foreign_keys(1)`:** Erzwingt referenzielle Integrität und automatische Kaskadierungs-Löschungen (`ON DELETE CASCADE`).
|
||||
|
||||
### 2. Connection Pool Begrenzung
|
||||
SQLite unterstützt nur einen einzigen Schreib-Thread zur gleichen Zeit:
|
||||
```go
|
||||
db.SetMaxOpenConns(8)
|
||||
db.SetMaxIdleConns(4)
|
||||
db.SetConnMaxLifetime(time.Hour)
|
||||
```
|
||||
|
||||
### 3. Cursor-Deadlock-Vermeidung
|
||||
> **Kritische Backend-Regel:** Niemals verschachtelte Abfragen bei offenem `*sql.Rows`-Cursor ausführen!
|
||||
In Go hält ein offener Cursor eine Verbindung aus dem Pool besetzt. Führt der Code innerhalb von `for rows.Next()` eine weitere Abfrage auf `s.db` aus, kann der Connection-Pool bei mehreren parallelen Benutzern innerhalb von Millisekunden einfrieren.
|
||||
*Pattern:* Alle Zeilen werden sofort vollständig in ein Go-Slice gelesen, `rows.Close()` wird ausgeführt, und erst danach werden verknüpfte Entitäten geladen.
|
||||
|
||||
---
|
||||
|
||||
## 🌐 5. Deployment & Container-Architektur
|
||||
|
||||
MindSift wird als schlanker, autarker Docker-Container ausgeliefert:
|
||||
1. **Stage 1 (Frontend Build):** `node:20-alpine` kompiliert TypeScript und generiert statische Assets nach `frontend/dist/`.
|
||||
2. **Stage 2 (Backend Build):** `golang:1.23-alpine` bettet `frontend/dist/` via `//go:embed` ein, führt Unit-Tests aus und kompiliert ein statisches Binary (`mindsift-server`).
|
||||
3. **Stage 3 (Production Runtime):** `alpine:3.20` mit `ca-certificates` und `tzdata`. Das finale Image ist ca. 45 MB groß und benötigt keine externen Datenbankserver oder Abhängigkeiten.
|
||||
|
||||
Alle persistenten Daten befinden sich in einem einzigen Verzeichnis `/data`:
|
||||
- `/data/mindsift.db` (SQLite Hauptdatenbank inklusive WAL-Journal und Shared-Memory-Dateien)
|
||||
- Notizen und Workspace-Blöcke werden zu 100% in der SQLite-Datenbank persistiert (keine losen Markdown-Dateien auf der Festplatte).
|
||||
Die Tests laufen beim Image-Build (`go test -v ./...`); ein Fehler bricht den Build ab. Frontend-Tests existieren nicht ([FLAW-19](flaws.md)).
|
||||
Reference in new issue
Block a user