Files
thomasandClaude 0371aa85a5 Multi-User-Plattform: jeder Benutzer hat eigene, isolierte Daten
- Auth via Session-Cookie + Login-Seite (scrypt, lib/password.js, sessions-Tabelle)
- AsyncLocalStorage (lib/context.js) propagiert aktuellen Benutzer durch alle Libs
- user_id auf allen Datentabellen (FK->users ON DELETE CASCADE), per-user PK/UNIQUE
  (app_state, settings, prompts, design, jobangebote) und per-user Dateispeicher
  (data/<dir>/<userId>/)
- Alle Queries in server.js + lib/api.js nach user_id scope-iert
- Pro-Benutzer-Konfiguration (Ollama/Mail/CalDAV/API-Token) in app_state,
  Live gelesen via config.get(); Hintergrund-Loops (IMAP/CalDAV) iterieren alle Benutzer
- REST-API /api/v1: X-API-Key loest den Token zu einem Benutzer auf, Anfragen
  operieren nur auf dessen Daten
- Admin-Panel /admin: Benutzer anlegen, Passwort zuruecksetzen, loeschen (mit Daten)
- Idempotente Migration (lib/migrate-multiuser.js + scripts/migrate-to-multiuser.js):
  bestehende Daten werden dem Benutzer admin:admin zugeordnet

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 21:57:50 +02:00

32 lines
1.4 KiB
JavaScript

// Per-request user context.
//
// The web UI authenticates a user via a signed session cookie (see the
// /login flow + auth middleware in server.js). The middleware stores the
// resolved user object in an AsyncLocalStorage, so any code running during the
// request — including the libs (config, mailer, caldav, chat) called deep in
// the stack — can read the *current user* without threading it through every
// function signature. Background jobs (IMAP poll, CalDAV sync) set the same
// context per user while iterating, so each user's config/data is used in turn.
//
// This is the keystone of the multi-tenant split: config.get() reads the current
// user's cfg rows, and the per-user query helpers below filter every SELECT/INSERT
// by the current user, guaranteeing isolation between users.
const { AsyncLocalStorage } = require('async_hooks');
const userContext = new AsyncLocalStorage();
// The current user object ({ id, username, is_admin }) or null outside a request
// (e.g. during boot). Anything that needs the user id must call this and decide
// how to behave when it is absent.
function currentUser() {
return userContext.getStore() || null;
}
// Convenience: the current user's id, or null when no user is set.
function currentUserId() {
const u = currentUser();
return u ? u.id : null;
}
module.exports = { userContext, currentUser, currentUserId };