Stand vor Einführung des Nacht-Agenten

This commit is contained in:
Kundencenter 2026-09-27 00:51:32 +02:00
commit 4763548bfb
168 changed files with 12726 additions and 0 deletions

152
migrations/001_core.sql Normal file
View file

@ -0,0 +1,152 @@
-- Kundencenter Grundschema (MariaDB). utf8mb4_unicode_ci.
CREATE TABLE settings (
`key` VARCHAR(100) PRIMARY KEY,
`value` VARCHAR(1000) NOT NULL,
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
);
INSERT INTO settings (`key`,`value`) VALUES ('registration_enabled','false');
CREATE TABLE users (
id CHAR(36) PRIMARY KEY,
email VARCHAR(254) NOT NULL UNIQUE,
name VARCHAR(150) NOT NULL,
password_hash VARCHAR(255) NULL,
kind ENUM('customer','staff') NOT NULL DEFAULT 'customer',
staff_role ENUM('support','accounting','admin','superadmin') NULL,
status ENUM('active','disabled','invited') NOT NULL DEFAULT 'invited',
email_verified_at DATETIME(3) NULL,
failed_logins INT NOT NULL DEFAULT 0,
locked_until DATETIME(3) NULL,
locale VARCHAR(10) NOT NULL DEFAULT 'de',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
CHECK ((kind = 'staff' AND staff_role IS NOT NULL) OR (kind = 'customer' AND staff_role IS NULL))
);
CREATE TABLE sessions (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
token_hash CHAR(64) NOT NULL UNIQUE,
csrf_token CHAR(43) NOT NULL,
mfa_verified TINYINT(1) NOT NULL DEFAULT 0,
pending_mfa TINYINT(1) NOT NULL DEFAULT 0,
ip VARCHAR(45) NULL,
user_agent VARCHAR(255) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
last_seen_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
expires_at DATETIME(3) NOT NULL,
revoked_at DATETIME(3) NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX (user_id)
);
CREATE TABLE mfa_totp (
user_id CHAR(36) PRIMARY KEY,
secret_enc VARCHAR(512) NOT NULL,
confirmed_at DATETIME(3) NULL,
last_step BIGINT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE recovery_codes (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id CHAR(36) NOT NULL,
code_hash CHAR(64) NOT NULL,
used_at DATETIME(3) NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX (user_id)
);
CREATE TABLE organizations (
id CHAR(36) PRIMARY KEY,
customer_number VARCHAR(20) NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
status ENUM('active','suspended','closed') NOT NULL DEFAULT 'active',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
);
CREATE TABLE customer_sequences (id TINYINT PRIMARY KEY, next_value INT NOT NULL);
INSERT INTO customer_sequences VALUES (1, 10000);
CREATE TABLE billing_profiles (
org_id CHAR(36) PRIMARY KEY,
company VARCHAR(200) NULL,
contact_name VARCHAR(150) NULL,
street VARCHAR(200) NULL,
zip VARCHAR(20) NULL,
city VARCHAR(100) NULL,
country CHAR(2) NOT NULL DEFAULT 'DE',
vat_id VARCHAR(30) NULL,
billing_email VARCHAR(254) NULL,
phone VARCHAR(50) NULL,
FOREIGN KEY (org_id) REFERENCES organizations(id) ON DELETE CASCADE
);
CREATE TABLE memberships (
org_id CHAR(36) NOT NULL,
user_id CHAR(36) NOT NULL,
role ENUM('owner','admin','member') NOT NULL DEFAULT 'member',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (org_id, user_id),
FOREIGN KEY (org_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Einmal-Token: Einladung, Passwort-Reset, E-Mail-Verifizierung
CREATE TABLE user_tokens (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
purpose ENUM('invite','password_reset','verify_email') NOT NULL,
token_hash CHAR(64) NOT NULL UNIQUE,
expires_at DATETIME(3) NOT NULL,
used_at DATETIME(3) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE audit_events (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
ts DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
actor_type ENUM('user','system','anonymous') NOT NULL,
actor_id CHAR(36) NULL,
org_id CHAR(36) NULL,
action VARCHAR(100) NOT NULL,
resource_type VARCHAR(50) NULL,
resource_id VARCHAR(64) NULL,
result ENUM('success','denied','failure') NOT NULL,
error_class VARCHAR(100) NULL,
connector VARCHAR(50) NULL,
correlation_id CHAR(36) NULL,
ip VARCHAR(45) NULL,
before_json JSON NULL,
after_json JSON NULL,
prev_hash CHAR(64) NOT NULL,
hash CHAR(64) NOT NULL,
INDEX (ts), INDEX (actor_id), INDEX (org_id), INDEX (action)
);
-- Persistente Aufträge (Outbox), Basis für Worker/Discord; später Provider-Jobs
CREATE TABLE jobs (
id CHAR(36) PRIMARY KEY,
type VARCHAR(100) NOT NULL,
payload JSON NOT NULL,
idempotency_key VARCHAR(150) NULL UNIQUE,
status ENUM('scheduled','running','waiting_external','succeeded','partial','failed','retrying','needs_review') NOT NULL DEFAULT 'scheduled',
attempts INT NOT NULL DEFAULT 0,
max_attempts INT NOT NULL DEFAULT 5,
run_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
locked_at DATETIME(3) NULL,
last_error VARCHAR(500) NULL,
correlation_id CHAR(36) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
INDEX (status, run_at)
);
CREATE TABLE mail_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
to_email VARCHAR(254) NOT NULL,
template VARCHAR(60) NOT NULL,
status ENUM('sent','failed','not_configured') NOT NULL,
error VARCHAR(300) NULL,
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);