-- Papa-Kind-Treff – Basis-Schema (MySQL 8) -- Hinweise: -- - Passwörter mit Argon2id hashen. -- - Sensible Felder werden app-seitig mit libsodium (XChaCha20-Poly1305) verschlüsselt -- und als base64 in VARBINARY-Spalten abgelegt (nonce + cipher). -- - Konto-E-Mails werden app-seitig verschlüsselt gespeichert; für die technische Suche -- und Dublettenprüfung wird zusätzlich ein HMAC-SHA-256-Lookup-Hash abgelegt. -- - share_level steuert Papa-Infos (basic, papa, papa_contact). -- - children_visibility steuert Kinder-Infos separat (hidden, age_only, details). CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARBINARY(512) NOT NULL, email_lookup_hash CHAR(64) NOT NULL, password_hash VARCHAR(255) NOT NULL, status ENUM('active','pending','blocked') DEFAULT 'pending', email_verified_at DATETIME NULL, last_login_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uq_users_email_lookup_hash (email_lookup_hash) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE user_profiles ( user_id BIGINT UNSIGNED PRIMARY KEY, display_name VARCHAR(120) NOT NULL, first_name VARBINARY(512) NULL, last_name VARBINARY(512) NULL, street VARBINARY(512) NULL, share_level ENUM('basic','papa','papa_contact') NOT NULL DEFAULT 'basic', children_visibility ENUM('hidden','age_only','details') NOT NULL DEFAULT 'hidden', zip CHAR(5) NULL, city VARCHAR(120) NULL, region VARCHAR(120) NULL, lat DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL, location_tracking_preference ENUM('disabled','prompt','enabled') NOT NULL DEFAULT 'prompt', avatar_style VARCHAR(40) NOT NULL DEFAULT 'lorelei', avatar_seed VARCHAR(100) NOT NULL DEFAULT '', avatar_config_json TEXT NULL, avatar_preset VARCHAR(64) NOT NULL DEFAULT 'papa-kind-treff', avatar_lorelei_eyes_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_eyebrows_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_mouth_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_glasses_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_hair_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_beard_variant VARCHAR(24) NOT NULL DEFAULT '', avatar_lorelei_earrings_variant VARCHAR(24) NOT NULL DEFAULT '', contact_phone VARBINARY(512) NULL, contact_email VARBINARY(512) NULL, profession VARBINARY(512) NULL, languages VARBINARY(1024) NULL, -- JSON verschlüsselt about VARBINARY(2048) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_profile_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_profile_city (city), INDEX idx_profile_region (region), INDEX idx_profile_latlng (lat,lng) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE children ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, gender ENUM('male','female','diverse','unknown') NOT NULL DEFAULT 'unknown', birthdate DATE NULL, age_years TINYINT UNSIGNED NULL, encrypted_first_name VARBINARY(512) NOT NULL, note VARBINARY(1024) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_child_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_child_user (user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE events ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, created_by BIGINT UNSIGNED NOT NULL, title VARCHAR(200) NOT NULL, teaser_public VARCHAR(280) NOT NULL, description TEXT NOT NULL, category_slug VARCHAR(120) NULL, image_path VARCHAR(255) NULL, location_label VARCHAR(180) NULL, location_source_type ENUM('custom','place','editorial_event') NOT NULL DEFAULT 'custom', location_source_listing_id BIGINT UNSIGNED NULL, street VARCHAR(180) NULL, zip CHAR(5) NULL, city VARCHAR(120) NULL, region VARCHAR(120) NULL, lat DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL, starts_at DATETIME NOT NULL, ends_at DATETIME NULL, max_participants SMALLINT UNSIGNED NULL, allow_kids TINYINT(1) NOT NULL DEFAULT 1, min_child_age TINYINT UNSIGNED NULL, max_child_age TINYINT UNSIGNED NULL, visibility ENUM('public','members') NOT NULL DEFAULT 'public', status ENUM('draft','published','cancelled') NOT NULL DEFAULT 'published', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_event_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_event_city (city), INDEX idx_event_region (region), INDEX idx_event_time (starts_at), INDEX idx_event_latlng (lat,lng) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE event_participants ( event_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, status ENUM('going','interested','waitlist','cancelled') NOT NULL DEFAULT 'going', child_count TINYINT UNSIGNED NULL, note VARBINARY(1024) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (event_id, user_id), CONSTRAINT fk_ep_event FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE, CONSTRAINT fk_ep_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE system_settings ( `key` VARCHAR(120) NOT NULL PRIMARY KEY, `value` TEXT NULL, updated_by BIGINT UNSIGNED NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_system_settings_updated_by FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_categories ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, slug VARCHAR(120) NOT NULL UNIQUE, title VARCHAR(160) NOT NULL, category_group ENUM('general','event','place','food','family','partner') NOT NULL DEFAULT 'general', sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_places ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, created_by BIGINT UNSIGNED NULL, source_type ENUM('user','partner','admin','system') NOT NULL DEFAULT 'user', title VARCHAR(180) NOT NULL, description TEXT NULL, street VARCHAR(180) NULL, zip CHAR(5) NULL, city VARCHAR(120) NULL, region VARCHAR(120) NULL, lat DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL, website_url VARCHAR(255) NULL, phone VARCHAR(60) NULL, place_kind VARCHAR(80) NULL, opening_hours_note TEXT NULL, opening_hours_json LONGTEXT NULL, provider_hint ENUM('manual','osm','google') NOT NULL DEFAULT 'manual', external_place_id VARCHAR(190) NULL, google_place_id VARCHAR(190) NULL, rating_value DECIMAL(3,2) NULL, rating_count INT UNSIGNED NULL, status ENUM('draft','published','archived') NOT NULL DEFAULT 'published', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listing_places_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_listing_places_city (city), INDEX idx_listing_places_region (region), INDEX idx_listing_places_latlng (lat,lng), INDEX idx_listing_places_kind (place_kind) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listings ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, created_by BIGINT UNSIGNED NULL, owner_type ENUM('user','partner','admin','system') NOT NULL DEFAULT 'user', listing_type ENUM('event','partner_offer','place','editorial_event') NOT NULL DEFAULT 'event', primary_place_id BIGINT UNSIGNED NULL, title VARCHAR(200) NOT NULL, teaser_public VARCHAR(280) NOT NULL, description TEXT NOT NULL, image_path VARCHAR(255) NULL, special_conditions_note TEXT NULL, visibility ENUM('public','members') NOT NULL DEFAULT 'public', status ENUM('draft','published','cancelled','archived') NOT NULL DEFAULT 'draft', supports_registration TINYINT(1) NOT NULL DEFAULT 0, supports_capacity TINYINT(1) NOT NULL DEFAULT 0, supports_pricing TINYINT(1) NOT NULL DEFAULT 0, is_recurring TINYINT(1) NOT NULL DEFAULT 0, legacy_event_id BIGINT UNSIGNED NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listings_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL, CONSTRAINT fk_listings_place FOREIGN KEY (primary_place_id) REFERENCES listing_places(id) ON DELETE SET NULL, CONSTRAINT fk_listings_legacy_event FOREIGN KEY (legacy_event_id) REFERENCES events(id) ON DELETE SET NULL, INDEX idx_listings_type (listing_type), INDEX idx_listings_status (status), INDEX idx_listings_owner (owner_type) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_category_map ( listing_id BIGINT UNSIGNED NOT NULL, category_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (listing_id, category_id), CONSTRAINT fk_lcm_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE, CONSTRAINT fk_lcm_category FOREIGN KEY (category_id) REFERENCES listing_categories(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_occurrences ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, listing_id BIGINT UNSIGNED NOT NULL, occurrence_type ENUM('single','series','range','open_ended') NOT NULL DEFAULT 'single', starts_at DATETIME NULL, ends_at DATETIME NULL, recurrence_rule VARCHAR(255) NULL, recurrence_until DATETIME NULL, capacity_total SMALLINT UNSIGNED NULL, booking_url VARCHAR(255) NULL, status ENUM('scheduled','cancelled','sold_out') NOT NULL DEFAULT 'scheduled', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listing_occurrences_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE, INDEX idx_listing_occurrences_start (starts_at), INDEX idx_listing_occurrences_type (occurrence_type), INDEX idx_listing_occurrences_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_prices ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, listing_id BIGINT UNSIGNED NOT NULL, occurrence_id BIGINT UNSIGNED NULL, label VARCHAR(120) NOT NULL, audience ENUM('general','adult','child','family','group') NOT NULL DEFAULT 'general', price_type ENUM('free','fixed','from','up_to','range','request') NOT NULL DEFAULT 'fixed', amount DECIMAL(10,2) NULL, amount_secondary DECIMAL(10,2) NULL, currency CHAR(3) NOT NULL DEFAULT 'EUR', note VARCHAR(255) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listing_prices_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE, CONSTRAINT fk_listing_prices_occurrence FOREIGN KEY (occurrence_id) REFERENCES listing_occurrences(id) ON DELETE CASCADE, INDEX idx_listing_prices_listing (listing_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_benefits ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, listing_id BIGINT UNSIGNED NOT NULL, occurrence_id BIGINT UNSIGNED NULL, benefit_type ENUM('voucher_code','voucher_hint','discount_text') NOT NULL DEFAULT 'voucher_hint', title VARCHAR(160) NOT NULL, code VARCHAR(120) NULL, description TEXT NULL, valid_from DATETIME NULL, valid_until DATETIME NULL, is_public TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listing_benefits_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE, CONSTRAINT fk_listing_benefits_occurrence FOREIGN KEY (occurrence_id) REFERENCES listing_occurrences(id) ON DELETE CASCADE, INDEX idx_listing_benefits_listing (listing_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE listing_moderation_requests ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, listing_id BIGINT UNSIGNED NOT NULL, request_type ENUM('create','update','delete') NOT NULL, request_status ENUM('open','approved','rejected') NOT NULL DEFAULT 'open', requested_by BIGINT UNSIGNED NOT NULL, reviewed_by BIGINT UNSIGNED NULL, request_reason TEXT NULL, review_note TEXT NULL, payload_json LONGTEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, reviewed_at DATETIME NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_listing_moderation_listing FOREIGN KEY (listing_id) REFERENCES listings(id) ON DELETE CASCADE, CONSTRAINT fk_listing_moderation_requested_by FOREIGN KEY (requested_by) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_listing_moderation_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_listing_moderation_status (request_status), INDEX idx_listing_moderation_type (request_type), INDEX idx_listing_moderation_user (requested_by) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Community / Forum CREATE TABLE forum_categories ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, slug VARCHAR(120) NOT NULL UNIQUE, title VARCHAR(180) NOT NULL, sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE forum_boards ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, category_id BIGINT UNSIGNED NOT NULL, slug VARCHAR(120) NOT NULL UNIQUE, title VARCHAR(180) NOT NULL, description TEXT NULL, icon VARCHAR(32) NULL, sort_order SMALLINT UNSIGNED NOT NULL DEFAULT 0, CONSTRAINT fk_fb_category FOREIGN KEY (category_id) REFERENCES forum_categories(id) ON DELETE CASCADE, INDEX idx_fb_category (category_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE forum_threads ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, board_id BIGINT UNSIGNED NULL, user_id BIGINT UNSIGNED NOT NULL, title VARCHAR(200) NOT NULL, body TEXT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_ft_board FOREIGN KEY (board_id) REFERENCES forum_boards(id) ON DELETE SET NULL, CONSTRAINT fk_ft_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_ft_created (created_at), INDEX idx_ft_board (board_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE forum_posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, thread_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, body TEXT NOT NULL, highlighted_by BIGINT UNSIGNED NULL, highlighted_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_fp_thread FOREIGN KEY (thread_id) REFERENCES forum_threads(id) ON DELETE CASCADE, CONSTRAINT fk_fp_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_fp_highlighted_by FOREIGN KEY (highlighted_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_fp_thread (thread_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE forum_post_feedback ( post_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, value TINYINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (post_id, user_id), CONSTRAINT fk_fpf_post FOREIGN KEY (post_id) REFERENCES forum_posts(id) ON DELETE CASCADE, CONSTRAINT fk_fpf_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_fpf_value (value) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE forum_reports ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, reporter_user_id BIGINT UNSIGNED NOT NULL, target_type ENUM('thread','post') NOT NULL, target_id BIGINT UNSIGNED NOT NULL, reason TEXT NOT NULL, status ENUM('open','resolved','dismissed') NOT NULL DEFAULT 'open', moderator_user_id BIGINT UNSIGNED NULL, moderator_note TEXT NULL, resolved_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_fr_reporter FOREIGN KEY (reporter_user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_fr_moderator FOREIGN KEY (moderator_user_id) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_fr_status (status), INDEX idx_fr_target (target_type, target_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE user_roles ( user_id BIGINT UNSIGNED NOT NULL, role ENUM('forum_admin','site_admin','owner') NOT NULL, assigned_by BIGINT UNSIGNED NULL, assigned_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, role), CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_ur_assigned_by FOREIGN KEY (assigned_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE community_admin_applications ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, motivation TEXT NOT NULL, status ENUM('open','approved','rejected') NOT NULL DEFAULT 'open', decision_reason TEXT NULL, decided_by BIGINT UNSIGNED NULL, decided_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_caa_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_caa_decided_by FOREIGN KEY (decided_by) REFERENCES users(id) ON DELETE SET NULL, INDEX idx_caa_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE community_user_restrictions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, restriction_type ENUM('thread_create_blocked','reply_blocked') NOT NULL, reason TEXT NULL, active TINYINT(1) NOT NULL DEFAULT 1, created_by BIGINT UNSIGNED NOT NULL, expires_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_cur_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_cur_created_by FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_cur_user_active (user_id, active) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Punkte-Tracking (persistent, config-Änderungen wirken nur auf neue Einträge) CREATE TABLE user_points ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, action VARCHAR(64) NOT NULL, amount DECIMAL(10,2) NOT NULL, meta JSON NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_up_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_up_user_action (user_id, action), INDEX idx_up_created (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE user_points_totals ( user_id BIGINT UNSIGNED PRIMARY KEY, total DECIMAL(12,2) NOT NULL DEFAULT 0, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_upt_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Session-Handling (neutral, keine sensiblen Inhalte) CREATE TABLE sessions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, token_hash CHAR(64) NOT NULL UNIQUE, -- SHA-256 Hash des Session-Tokens ip VARCHAR(45) NULL, user_agent VARCHAR(255) NULL, expires_at DATETIME NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_session_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_session_expires (expires_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Kurzlebige Tokens (Passwort-Reset, Magic-Login, E-Mail-Verify) CREATE TABLE user_tokens ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, type ENUM('reset','verify','magic_login') NOT NULL, code CHAR(12) NULL, -- z. B. 6-stelliger Code (optional Klartext) token_hash CHAR(64) NOT NULL UNIQUE, expires_at DATETIME NOT NULL, used_at DATETIME NULL, ip VARCHAR(45) NULL, user_agent VARCHAR(255) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_ut_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_ut_expires (expires_at), INDEX idx_ut_type (type) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE user_calendar_feeds ( user_id BIGINT UNSIGNED NOT NULL PRIMARY KEY, token_encrypted TEXT NOT NULL, token_lookup_hash CHAR(64) NOT NULL UNIQUE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, rotated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_accessed_at DATETIME NULL, CONSTRAINT fk_user_calendar_feed_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_user_calendar_feeds_lookup (token_lookup_hash) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Audit-Log für wichtige Aktionen CREATE TABLE audit_log ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NULL, -- NULL bei Gast-Ereignissen action VARCHAR(100) NOT NULL, -- z.B. login, profile.update, event.create target_type VARCHAR(50) NULL, -- z.B. user, event, child target_id BIGINT UNSIGNED NULL, metadata JSON NULL, -- nicht sensible Zusatzinfos ip VARCHAR(45) NULL, user_agent VARCHAR(255) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_audit_user (user_id), INDEX idx_audit_action (action), INDEX idx_audit_target (target_type, target_id), INDEX idx_audit_created (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;