- 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>
32 lines
1.4 KiB
JavaScript
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 }; |