Vollständiges Zugangs-/Rollen-/Rechtesystem gebaut (Anforderungsdokument): D1-Datenbank, AES-verschlüsselte Codes nur für Owner einsehbar, granulare Permissions, Session-Auth mit Lockout, Zugänge-&-Teammitglieder-Verwaltungsseite, Bewerbungen mit Status/Notizen/Entwürfen

This commit is contained in:
2026-07-31 00:37:04 +02:00
parent 96087f460e
commit 6a1c0e355c
18 changed files with 2283 additions and 539 deletions
+106
View File
@@ -0,0 +1,106 @@
-- =====================================================================
-- DOGFATHER UNIVERSE — Interner Bereich: Datenbankschema
-- Setzt "Geschuetzter_Zugang_Rollen_Benutzerverwaltung.pdf" um.
-- =====================================================================
-- Rollen: frei erstellbar/umbenennbar durch den Hauptadministrator (Owner).
-- Die Owner-Rolle selbst ist NICHT in dieser Tabelle — sie ist fest an das
-- POSTFACH_CODE-Secret gebunden und kann nicht gelöscht/verändert werden.
CREATE TABLE IF NOT EXISTS roles (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
permissions TEXT NOT NULL DEFAULT '[]', -- JSON-Array erlaubter Permission-Keys
created_at TEXT NOT NULL
);
-- Zugänge/Teammitglieder. Jede Person hat einen eigenen Code.
-- code_hash: SHA-256 für schnellen Login-Vergleich (nicht umkehrbar).
-- code_enc: AES-GCM verschlüsselter Klartext-Code, NUR vom Owner nach
-- Re-Auth entschlüsselbar (siehe crypto.js). Wird niemals an
-- andere Personen oder über die API an Nicht-Owner ausgegeben.
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
username TEXT NOT NULL UNIQUE,
email TEXT,
role_id TEXT REFERENCES roles(id),
code_hash TEXT,
code_enc TEXT,
status TEXT NOT NULL DEFAULT 'pending', -- pending | active | locked
note TEXT,
expires_at TEXT, -- NULL = dauerhaft
created_at TEXT NOT NULL,
created_by TEXT NOT NULL,
last_login_at TEXT
);
-- Individuelle Berechtigungs-Ausnahmen pro Person (haben Vorrang vor der Rolle).
CREATE TABLE IF NOT EXISTS user_permission_overrides (
user_id TEXT NOT NULL REFERENCES users(id),
permission TEXT NOT NULL,
allowed INTEGER NOT NULL, -- 1 = zusätzlich erlaubt, 0 = entzogen
PRIMARY KEY (user_id, permission)
);
-- Sessions: token wird gehasht gespeichert (wie ein Passwort), nie im Klartext.
CREATE TABLE IF NOT EXISTS sessions (
token_hash TEXT PRIMARY KEY,
user_id TEXT NOT NULL, -- 'owner' oder users.id
is_owner INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
last_active_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
-- Aktivitätsprotokoll — Codes erscheinen hier NIEMALS im Klartext.
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
actor TEXT NOT NULL,
action TEXT NOT NULL,
target TEXT,
detail TEXT,
created_at TEXT NOT NULL
);
-- Login-Schutz: Fehlversuche pro Schlüssel (IP oder Benutzername) zählen,
-- nach zu vielen Versuchen vorübergehend sperren.
CREATE TABLE IF NOT EXISTS login_attempts (
key TEXT PRIMARY KEY,
count INTEGER NOT NULL DEFAULT 0,
locked_until TEXT,
updated_at TEXT NOT NULL
);
-- Bewerbungen (bisher in KV, jetzt in D1 für Status/Zuweisung/Notizen/Entwürfe).
CREATE TABLE IF NOT EXISTS applications (
id TEXT PRIMARY KEY,
type TEXT NOT NULL, -- creator | scout | kooperation
data TEXT NOT NULL, -- JSON der Formularfelder
status TEXT NOT NULL DEFAULT 'neu', -- neu | in_bearbeitung | beantwortet
assigned_to TEXT, -- users.id oder 'owner'
archived INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS application_notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
application_id TEXT NOT NULL REFERENCES applications(id),
author TEXT NOT NULL,
text TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS application_drafts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
application_id TEXT NOT NULL REFERENCES applications(id),
author TEXT NOT NULL,
content TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);
CREATE INDEX IF NOT EXISTS idx_applications_status ON applications(status, archived);
CREATE INDEX IF NOT EXISTS idx_notes_app ON application_notes(application_id);
CREATE INDEX IF NOT EXISTS idx_drafts_app ON application_drafts(application_id);