-- Login-Sperre, Sitzungszweck (Vollzugriff / 2FA-Einrichtung / 2FA-Challenge), TOTP. ALTER TABLE profiles ADD COLUMN IF NOT EXISTS failed_login_count INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS locked_until TIMESTAMP, ADD COLUMN IF NOT EXISTS totp_secret TEXT, ADD COLUMN IF NOT EXISTS totp_pending_secret TEXT, ADD COLUMN IF NOT EXISTS totp_enabled BOOLEAN NOT NULL DEFAULT FALSE; ALTER TABLE sessions ADD COLUMN IF NOT EXISTS purpose VARCHAR(32) NOT NULL DEFAULT 'full', ADD COLUMN IF NOT EXISTS failed_attempts INTEGER NOT NULL DEFAULT 0; UPDATE sessions SET purpose = 'password_reset' WHERE token LIKE 'reset_%' AND purpose = 'full'; CREATE INDEX IF NOT EXISTS idx_sessions_profile_purpose ON sessions (profile_id, purpose); CREATE TABLE IF NOT EXISTS profile_recovery_codes ( id SERIAL PRIMARY KEY, profile_id INTEGER NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, code_hash VARCHAR(64) NOT NULL, used_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_profile_recovery_codes_profile ON profile_recovery_codes (profile_id);