Architektur
Überblick
Abschnitt betitelt „Überblick“Orimora ist ein Full-Stack-Monolith auf Basis von SvelteKit. Es gibt keine Microservices: SvelteKit-Server-Routen sind die API, derselbe Node.js-Prozess dient dem Frontend, verarbeitet WebSocket-Verbindungen für kollaboratives Editieren und führt Hintergrundjobs aus.
flowchart TD
Browser["Browser-Client\nSvelteKit SSR · Svelte 5 Runes\nTipTap · Yjs · Hocuspocus"]
subgraph Node["Node.js-Anwendung (Port 3000)"]
SK["SvelteKit\nHTTP-Routen / API"]
HC["Hocuspocus\nWebSocket /collab"]
BQ["BullMQ Worker\n(Hintergrundjobs)"]
end
Browser -- "HTTP / SSR" --> SK
Browser -- "WebSocket" --> HC
SK -- "Drizzle ORM" --> PG[("PostgreSQL 16\npg_trgm · Volltextsuche")]
HC -- "Yjs-Zustand" --> PG
BQ -- "Jobs" --> PG
SK -- "Sessions · Cache · Rate-Limit" --> RD[("Redis 7")]
BQ -- "Queue" --> RD
SK -- "S3 SDK (optional)" --> S3[("S3 / Object Storage\nUploads · Exports")]
BQ -- "SMTP" --> Mail["Mailserver\n(SMTP)"]
BQ -- "HTTP POST" --> Hooks["Ausgehende Webhooks\n& Publishing-Kanäle"]
Request-Lifecycle (vereinfacht):
- Browser sendet HTTP-Request → Traefik (TLS-Terminierung) → Node.js
- SvelteKit-Route authentifiziert Session (Redis) und prüft Berechtigungen
- Service fragt Drizzle ORM → PostgreSQL ab
- Antwort als JSON serialisiert (API-Routen) oder als HTML gerendert (Seiten-Routen)
- Nebeneffekte (Webhooks, Benachrichtigungen, Backlinks) werden per BullMQ asynchron ausgelöst
Verzeichnisstruktur
Abschnitt betitelt „Verzeichnisstruktur“src/├── lib/│ ├── components/ # Svelte-UI-Komponenten│ │ └── editor/ # TipTap-Editor + Extensions│ ├── server/ # Nur Server-Code│ │ ├── db/ # Drizzle-Schema + Migrationen│ │ ├── services/ # Business-Logik│ │ └── policies/ # Autorisierungsprüfungen│ └── utils/ # Geteilte Hilfsfunktionen├── routes/│ ├── (app)/ # Authentifizierte App-Routen│ ├── (auth)/ # Login / Registrierung│ └── api/ # API-Endpunkte (RPC + /v1/ REST)└── hooks.server.ts # Auth, Rate-Limit, Security-HeaderDatenfluss
Abschnitt betitelt „Datenfluss“- Request trifft in
hooks.server.tsein — Rate-Limit, Session wird geladen - Route-Handler ruft eine Service-Funktion auf (z. B.
getDocument,createDocument) - Service fragt Drizzle ORM → PostgreSQL ab
- Antwort als JSON (API) oder HTML (Seiten) serialisiert
- Hintergrundeffekte (Webhooks, Benachrichtigungen, Backlinks) über BullMQ
Kollaboration
Abschnitt betitelt „Kollaboration“Echtzeit-Kollaboration nutzt Yjs (CRDT) mit Hocuspocus als Server:
- Jedes Dokument hat einen eindeutigen Raum (UUID)
- Der Yjs-Zustand wird über
@hocuspocus/extension-databasein PostgreSQL persistiert - Beim Verbinden lädt der Server den aktuellen Inhalt und hydratisiert den Yjs-Zustand
- Beim Trennen wird der finale Yjs-Zustand in die
documents-Tabelle geschrieben
Sicherheit
Abschnitt betitelt „Sicherheit“- Sessions: HTTP-only, Secure, SameSite=Lax Cookies mit zufälligen Tokens
- CSRF: Mutierende API-Requests aus dem Browser erfordern Session-Auth
- API-Keys: Präfix
kb_, verschlüsselt at rest, scope-begrenzt - CSP: Nonce-basierte Policy über SvelteKit
- CORS: Nur auf
/api/v1/*mitAuthorization-Header-Unterstützung