-- Deploymentcenter Unified Database Schema & Non-Destructive Migrations -- UTF-8 (utf8mb4) & InnoDB SET FOREIGN_KEY_CHECKS = 0; -- 1. Core Platform Tables CREATE TABLE IF NOT EXISTS dc_users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(64) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, totp_secret VARCHAR(64) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS dc_settings ( skey VARCHAR(64) PRIMARY KEY, svalue TEXT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Core Projects Table (Shared across Lizenzen, Watchdog, UpdateService) CREATE TABLE IF NOT EXISTS dc_projects ( id INT AUTO_INCREMENT PRIMARY KEY, slug VARCHAR(64) NOT NULL UNIQUE, name VARCHAR(190) NOT NULL, notes TEXT NULL, default_cache_ttl_hours INT NOT NULL DEFAULT 168, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 2. License Module Tables (Linked to dc_projects) CREATE TABLE IF NOT EXISTS license_licenses ( id INT AUTO_INCREMENT PRIMARY KEY, product_id INT NOT NULL, license_key CHAR(29) NOT NULL UNIQUE, customer_name VARCHAR(190) NULL, customer_email VARCHAR(190) NULL, status ENUM('active','revoked','suspended') NOT NULL DEFAULT 'active', expires_at DATETIME NULL, max_activations INT NOT NULL DEFAULT 2, notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (product_id) REFERENCES dc_projects(id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS license_activations ( id INT AUTO_INCREMENT PRIMARY KEY, license_id INT NOT NULL, hardware_id VARCHAR(128) NOT NULL, hwid_version TINYINT NOT NULL DEFAULT 1, hwid_source VARCHAR(32) NULL, platform VARCHAR(8) NULL, hostname VARCHAR(190) NULL, app_version VARCHAR(64) NULL, first_seen DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_seen DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, is_blocked TINYINT(1) NOT NULL DEFAULT 0, UNIQUE KEY uq_lic_hw (license_id, hardware_id), FOREIGN KEY (license_id) REFERENCES license_licenses(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS license_audit_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, actor VARCHAR(64) NOT NULL, action VARCHAR(64) NOT NULL, details TEXT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS license_api_rate_limit ( ip VARBINARY(16) NOT NULL, window_start DATETIME NOT NULL, request_count INT NOT NULL DEFAULT 0, PRIMARY KEY (ip, window_start) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3. Watchdog Module Tables CREATE TABLE IF NOT EXISTS watchdog_monitors ( id BIGINT AUTO_INCREMENT PRIMARY KEY, source VARCHAR(100) NOT NULL, instance VARCHAR(100) NOT NULL DEFAULT 'default', type ENUM('heartbeat','host','hypervisor_node','guest') NOT NULL DEFAULT 'heartbeat', state ENUM('up','warning','down','error','stopped','maintenance') NOT NULL DEFAULT 'up', expected_interval_sec INT NOT NULL DEFAULT 60, last_seen_utc DATETIME NULL, last_status ENUM('ok','warning','error') NULL, last_message TEXT NULL, metrics_json JSON NULL, metric_state_reason VARCHAR(255) NULL, group_key VARCHAR(150) NULL, parent_source VARCHAR(100) NULL, notes TEXT NULL, url VARCHAR(255) NULL, icon VARCHAR(255) NULL, is_muted TINYINT(1) NOT NULL DEFAULT 0, expect_running TINYINT(1) NOT NULL DEFAULT 1, os VARCHAR(50) NULL, first_contact_deadline_utc DATETIME NULL, ack_until_utc DATETIME NULL, acknowledged_by VARCHAR(100) NULL, warning_notified_utc DATETIME NULL, suppress_until_utc DATETIME NULL, last_started_utc DATETIME NULL, last_stopped_utc DATETIME NULL, last_crash_utc DATETIME NULL, created_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uq_monitor (source, instance) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS watchdog_event_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, source VARCHAR(100) NOT NULL, instance VARCHAR(100) NOT NULL DEFAULT 'default', kind ENUM('started','stopped_graceful','crash_suspected','hard_error', 'recovered','warning_raised','warning_cleared', 'maintenance_start','maintenance_end','watchdog_started') NOT NULL, from_state VARCHAR(20) NULL, to_state VARCHAR(20) NULL, severity ENUM('info','warning','alarm') NOT NULL DEFAULT 'info', at_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, message TEXT NULL, meta_json JSON NULL, notified TINYINT(1) NOT NULL DEFAULT 0, notified_at_utc DATETIME NULL, channel VARCHAR(40) NULL, KEY ix_evt (source, instance, at_utc), KEY ix_evt_time (at_utc) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS watchdog_cron_jobs ( name VARCHAR(50) PRIMARY KEY, interval_sec INT NOT NULL, last_run_utc DATETIME NULL, running TINYINT(1) NOT NULL DEFAULT 0, lock_until_utc DATETIME NULL, last_status VARCHAR(20) NULL, last_duration_ms INT NULL, enabled TINYINT(1) NOT NULL DEFAULT 1 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS watchdog_agent_tokens ( token_id VARCHAR(64) PRIMARY KEY, token_hash VARCHAR(128) NOT NULL, raw_token VARCHAR(128) NULL, name VARCHAR(100) NOT NULL, monitor_source VARCHAR(100) NULL, monitor_instance VARCHAR(100) NULL, created_at_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_used_at_utc DATETIME NULL, revoked TINYINT(1) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS watchdog_proxmox_targets ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, url VARCHAR(255) NOT NULL, token_id VARCHAR(150) NOT NULL, token_secret TEXT NOT NULL, cert_fingerprint VARCHAR(100) NULL, enabled TINYINT(1) NOT NULL DEFAULT 1, last_poll_utc DATETIME NULL, last_status VARCHAR(50) NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4. UpdateService Module Tables CREATE TABLE IF NOT EXISTS updateservice_releases ( id INT AUTO_INCREMENT PRIMARY KEY, product_slug VARCHAR(64) NOT NULL, version VARCHAR(32) NOT NULL, channel VARCHAR(32) NOT NULL DEFAULT 'prod', release_notes TEXT NULL, download_url VARCHAR(255) NOT NULL, sha256_hash VARCHAR(64) NULL, git_commit VARCHAR(64) NULL, size_bytes BIGINT NOT NULL DEFAULT 0, manifest_json JSON NULL, is_critical TINYINT(1) NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uq_prod_ver_chan (product_slug, version, channel) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5. Core Token Hierarchy & Bugtracker Tables CREATE TABLE IF NOT EXISTS dc_tokens ( id BIGINT AUTO_INCREMENT PRIMARY KEY, token_id VARCHAR(64) NOT NULL UNIQUE, parent_token_id VARCHAR(64) NULL, token_hash VARCHAR(128) NOT NULL, raw_token VARCHAR(128) NULL, name VARCHAR(100) NOT NULL, project_slug VARCHAR(64) NULL, license_key CHAR(29) NULL, owner_type ENUM('license', 'project', 'host', 'dev_agent', 'custom') NOT NULL DEFAULT 'custom', owner_identity VARCHAR(190) NULL, type ENUM('master', 'sub') NOT NULL DEFAULT 'sub', scopes JSON NOT NULL, environment ENUM('production', 'development', 'all') NOT NULL DEFAULT 'all', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_used_at DATETIME NULL, expires_at DATETIME NULL, revoked TINYINT(1) NOT NULL DEFAULT 0, FOREIGN KEY (parent_token_id) REFERENCES dc_tokens(token_id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS bugtracker_items ( id BIGINT AUTO_INCREMENT PRIMARY KEY, project_id INT NULL, project_slug VARCHAR(64) NOT NULL, type ENUM('bug', 'feature_request') NOT NULL DEFAULT 'bug', title VARCHAR(255) NOT NULL, description TEXT NULL, error_message TEXT NULL, stack_trace TEXT NULL, error_hash VARCHAR(64) NULL, build_version VARCHAR(64) NULL, environment ENUM('production', 'development', 'staging', 'testing') NOT NULL DEFAULT 'production', severity ENUM('low', 'medium', 'high', 'critical') NOT NULL DEFAULT 'medium', status ENUM('open', 'planned', 'in_progress', 'resolved', 'closed', 'rejected') NOT NULL DEFAULT 'open', occurrence_count INT NOT NULL DEFAULT 1, first_seen_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_seen_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, resolved_at DATETIME NULL, resolved_in_build VARCHAR(64) NULL, resolution_notes TEXT NULL, created_by VARCHAR(100) NOT NULL DEFAULT 'agent', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY ix_bt_proj_env (project_slug, environment, type, status), KEY ix_bt_hash (error_hash) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS bugtracker_comments ( id BIGINT AUTO_INCREMENT PRIMARY KEY, item_id BIGINT NOT NULL, author VARCHAR(100) NOT NULL, comment TEXT NOT NULL, action_taken VARCHAR(64) NULL, meta_json JSON NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (item_id) REFERENCES bugtracker_items(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SET FOREIGN_KEY_CHECKS = 1; -- Seed default cron jobs for Watchdog (Non-destructive) INSERT INTO watchdog_cron_jobs (name, interval_sec, enabled) VALUES ('evaluator', 60, 1), ('proxmox_poll', 60, 1), ('proxmox_smart', 600, 1), ('warning_digest', 43200, 1), ('self_ping', 60, 1), ('eventlog_cleanup', 86400, 1) ON DUPLICATE KEY UPDATE interval_sec = VALUES(interval_sec); -- Seed Initial Projects (Non-destructive) INSERT INTO dc_projects (id, slug, name, notes, default_cache_ttl_hours) VALUES (1, 'myapp', 'My Application Deluxe', 'Hauptanwendung für Desktop und Server', 168), (2, 'polytrader', 'PolyTrader Suite Pro', 'Trading- und Handelssystem Client', 72), (3, 'predictalytics', 'Predictalytics Engine', 'Datenanalyse und Vorhersage Dienst', 168) ON DUPLICATE KEY UPDATE id = id; -- Seed Initial Licenses (Non-destructive) INSERT INTO license_licenses (id, product_id, license_key, customer_name, customer_email, status, max_activations, notes) VALUES (1, 1, 'LLAB1-98A72-B3C4D-5E6F7-89012', 'Musterfirma GmbH', 'muster@example.com', 'active', 5, 'Firmenlizenz Premium'), (2, 2, 'LLAB2-12345-67890-ABCDE-FGHIJ', 'Software Solutions AG', 'info@softsol.de', 'active', 2, 'Pro-Lizenz Einzelnutzer'), (3, 3, 'LLAB3-44556-77889-99001-AABB2', 'Test User Corp', 'test@usercorp.com', 'revoked', 1, 'Testlizenz gekündigt') ON DUPLICATE KEY UPDATE id = id; -- Seed Initial Activations (Non-destructive) INSERT IGNORE INTO license_activations (license_id, hardware_id, hostname, app_version, is_blocked) VALUES (1, 'HWID-88A9-99B1-CC02', 'PROD-SERVER-01', 'v1.4.2', 0), (1, 'HWID-3344-5566-7788', 'DESKTOP-WIN11-DEV', 'v1.4.2', 0), (2, 'HWID-7766-5544-3322', 'TRADING-NODE-01', 'v2.0.1', 0); -- Seed Initial Audit Logs (Non-destructive) INSERT IGNORE INTO license_audit_log (id, actor, action, details) VALUES (1, 'admin', 'license.create', '{"product": "myapp", "license_key": "LLAB1-98A72-B3C4D-5E6F7-89012"}'), (2, 'api', 'api.validate.success', '{"product": "myapp", "hardware_id": "HWID-88A9-99B1-CC02"}'), (3, 'admin', 'license.revoke', '{"license_key": "LLAB3-44556-77889-99001-AABB2", "reason": "Vertrag abgelaufen"}'); -- Seed Initial Watchdog Monitors (Non-destructive) INSERT INTO watchdog_monitors (source, instance, type, state, expected_interval_sec, last_seen_utc, last_status, last_message, metrics_json, group_key, os) VALUES ('srv-db-01', 'default', 'host', 'up', 60, NOW(), 'ok', 'System OK - All services running', '{"cpu": 18, "ram": 42, "disk": 55}', 'Infrastructure', 'Ubuntu 24.04 LTS'), ('srv-app-01', 'default', 'host', 'up', 60, NOW(), 'ok', 'App Server Operational', '{"cpu": 34, "ram": 68, "disk": 40}', 'Infrastructure', 'Debian 12'), ('PolyTrader Worker', 'default', 'heartbeat', 'up', 60, NOW(), 'ok', 'Processing job queue #842', NULL, 'Applications', '.NET 8 Service'), ('License Server Sync', 'default', 'heartbeat', 'up', 120, NOW(), 'ok', 'Sync OK', NULL, 'Services', 'PHP 8.3 Cron'), ('Update Poller Service', 'default', 'heartbeat', 'warning', 60, NOW(), 'warning', 'High latency on remote node', NULL, 'Services', 'Python Service'), ('pve-node-01', 'default', 'hypervisor_node', 'up', 60, NOW(), 'ok', 'Hypervisor Node healthy', '{"cpu": 25, "ram": 52}', 'Proxmox Cluster', 'Proxmox VE 8.1'), ('vm-100-mail', 'default', 'guest', 'up', 60, NOW(), 'ok', 'Mail VM active', NULL, 'pve-node-01', 'Linux Mail Server'), ('vm-101-backup', 'default', 'guest', 'stopped', 300, NOW(), 'ok', 'Cold Backup VM stopped gracefully', NULL, 'pve-node-01', 'Cold Backup VM') ON DUPLICATE KEY UPDATE source = source; -- Seed Initial Watchdog Agent Tokens (Non-destructive) INSERT INTO watchdog_agent_tokens (token_id, token_hash, raw_token, name, monitor_source) VALUES ('tok_infra01', '140e6c7329ecb3ea662580a1495c654f593361e64cf96ec37050dd0915f013d5', 'wd_live_token_infra_01_secure', 'Infrastructure Agent Token', 'srv-db-01') ON DUPLICATE KEY UPDATE token_id = token_id; -- Seed Initial Update Releases (Non-destructive) INSERT INTO updateservice_releases (product_slug, version, release_notes, download_url, sha256_hash, is_critical) VALUES ('myapp', '1.0.0', 'Initiales Release mit Grundfunktionen', 'https://dc.mhdf.de/downloads/myapp-1.0.0.zip', 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 0), ('myapp', '1.1.0', 'Fehlerbehebungen und Performance-Optimierung', 'https://dc.mhdf.de/downloads/myapp-1.1.0.zip', '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', 0), ('polytrader', '2.0.1', 'Kritisches Sicherheits-Update für Handelsverbindungen', 'https://dc.mhdf.de/downloads/polytrader-2.0.1.zip', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 1) ON DUPLICATE KEY UPDATE product_slug = product_slug; -- Seed Initial Tokens (Non-destructive) INSERT INTO dc_tokens ( token_id, parent_token_id, token_hash, raw_token, name, project_slug, owner_type, owner_identity, type, scopes, environment ) VALUES ('tok_m_myapp_dev', NULL, '5a38a7c29e64e526c71c4c16a695123d6874e0d9bf0d768132049e8fa65e10aa', 'dc_master_myapp_dev_agent_001', 'MyApp Dev Workstation Master Key', 'myapp', 'dev_agent', 'DEV-WORKSTATION-01', 'master', '["bugtracker:report", "bugtracker:manage", "watchdog:ping", "updateservice:read"]', 'all'), ('tok_s_myapp_agent_sub1', 'tok_m_myapp_dev', '7c7bc0a5d4d3856b3e9447477c133a8a3a0e67617ab6f6d0a793a388b14a27bc', 'dc_sub_myapp_agent_live_001', 'Dev Agent Auto-Provisioned Token', 'myapp', 'dev_agent', 'DEV-WORKSTATION-01', 'sub', '["bugtracker:report", "bugtracker:manage"]', 'development') ON DUPLICATE KEY UPDATE token_id = token_id; -- Seed Initial Bugtracker Items (Non-destructive) INSERT INTO bugtracker_items ( id, project_slug, type, title, description, error_message, stack_trace, error_hash, build_version, environment, severity, status, occurrence_count, created_by ) VALUES (1, 'myapp', 'bug', 'NullReferenceException in UserAuthService.cs line 42', 'Beim Login ohne gesetzte Session ist ein Unerwarteter Nullpointer-Fehler aufgetreten.', 'NullReferenceException: Object reference not set to an instance of an object.', 'at MyApp.Core.UserAuthService.ValidateToken(String token) in UserAuthService.cs:line 42\nat MyApp.Controllers.AuthController.Login() in AuthController.cs:line 18', 'e2c918a514d89a42f', 'v1.4.2-dev', 'development', 'high', 'open', 3, 'agent:dev-monitor-01'), (2, 'polytrader', 'bug', 'Database Connection Timeout on Order Execution', 'Unter hoher Last bricht die Verbindung zur MariaDB nach 30s ab.', 'SQLSTATE[HY000] [2002] Connection timed out', 'PDOException: SQLSTATE[HY000] [2002] Connection timed out at Db.php:24', 'f9a2b881c1092837d', 'v2.0.1', 'production', 'critical', 'in_progress', 14, 'agent:prod-watchdog'), (3, 'myapp', 'feature_request', 'Unterstützung für TOTP 2FA Login erzwingen', 'KI-Agent empfiehlt die Hinzufügung einer erzwungenen Zwei-Faktor-Authentifizierung für Administrator-Konten.', NULL, NULL, NULL, 'v1.5.0-roadmap', 'development', 'medium', 'open', 1, 'agent:security-agent') ON DUPLICATE KEY UPDATE id = id; -- Seed Initial Comments (Non-destructive) INSERT INTO bugtracker_comments ( id, item_id, author, comment, action_taken, created_at ) VALUES (1, 1, 'agent:code-fixer-01', 'Log-Analyse gestartet. Der Fehler tritt auf, wenn $_SESSION["dc_user_id"] nicht gesetzt ist.', 'investigated', NOW()), (2, 2, 'agent:db-optimizer', 'Max Connection Limit in config.php auf 100 erhöht. Connection Pool wird beobachtet.', 'added_hint', NOW()) ON DUPLICATE KEY UPDATE id = id;