CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'viewer', created_at TEXT NOT NULL DEFAULT (datetime('now')), last_login TEXT, CHECK (role IN ('admin', 'viewer')) ); CREATE TABLE IF NOT EXISTS servers ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT, game_type TEXT NOT NULL DEFAULT 'arma3', status TEXT NOT NULL DEFAULT 'stopped', pid INTEGER, exe_path TEXT NOT NULL, started_at TEXT, stopped_at TEXT, game_port INTEGER NOT NULL, rcon_port INTEGER, auto_restart INTEGER NOT NULL DEFAULT 0, max_restarts INTEGER NOT NULL DEFAULT 3, restart_window_seconds INTEGER NOT NULL DEFAULT 300, restart_count INTEGER NOT NULL DEFAULT 0, last_restart_at TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), CHECK (status IN ('stopped','starting','running','stopping','crashed','error')), CHECK (game_port BETWEEN 1024 AND 65535), CHECK (rcon_port IS NULL OR (rcon_port BETWEEN 1024 AND 65535)) ); CREATE INDEX IF NOT EXISTS idx_servers_status ON servers(status); CREATE INDEX IF NOT EXISTS idx_servers_game_type ON servers(game_type); CREATE INDEX IF NOT EXISTS idx_servers_game_port ON servers(game_port); CREATE TABLE IF NOT EXISTS game_configs ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, game_type TEXT NOT NULL, section TEXT NOT NULL, config_json TEXT NOT NULL DEFAULT '{}', config_version INTEGER NOT NULL DEFAULT 1, schema_version TEXT NOT NULL DEFAULT '1.0.0', updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(server_id, section) ); CREATE INDEX IF NOT EXISTS idx_game_configs_server ON game_configs(server_id); CREATE INDEX IF NOT EXISTS idx_game_configs_type_section ON game_configs(game_type, section); CREATE TABLE IF NOT EXISTS mods ( id INTEGER PRIMARY KEY AUTOINCREMENT, game_type TEXT NOT NULL, name TEXT NOT NULL, folder_path TEXT NOT NULL, workshop_id TEXT, description TEXT, game_data TEXT DEFAULT '{}', created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (game_type, folder_path) ); CREATE TABLE IF NOT EXISTS server_mods ( server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, mod_id INTEGER NOT NULL REFERENCES mods(id) ON DELETE CASCADE, is_server_mod INTEGER NOT NULL DEFAULT 0, sort_order INTEGER NOT NULL DEFAULT 0, game_data TEXT DEFAULT '{}', PRIMARY KEY (server_id, mod_id) ); CREATE INDEX IF NOT EXISTS idx_server_mods_server ON server_mods(server_id); CREATE TABLE IF NOT EXISTS missions ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, filename TEXT NOT NULL, mission_name TEXT NOT NULL, terrain TEXT, file_size INTEGER, game_data TEXT DEFAULT '{}', uploaded_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (server_id, filename) ); CREATE INDEX IF NOT EXISTS idx_missions_server ON missions(server_id); CREATE TABLE IF NOT EXISTS mission_rotation ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, mission_id INTEGER NOT NULL REFERENCES missions(id) ON DELETE CASCADE, sort_order INTEGER NOT NULL DEFAULT 0, difficulty TEXT, params_json TEXT NOT NULL DEFAULT '{}', game_data TEXT DEFAULT '{}', UNIQUE (server_id, sort_order) ); CREATE INDEX IF NOT EXISTS idx_mission_rotation_server ON mission_rotation(server_id); CREATE TABLE IF NOT EXISTS players ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, slot_id TEXT NOT NULL, name TEXT NOT NULL, guid TEXT, ip TEXT, ping INTEGER, game_data TEXT DEFAULT '{}', joined_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (server_id, slot_id) ); CREATE INDEX IF NOT EXISTS idx_players_server ON players(server_id); CREATE TABLE IF NOT EXISTS player_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, name TEXT NOT NULL, guid TEXT, ip TEXT, game_data TEXT DEFAULT '{}', joined_at TEXT NOT NULL, left_at TEXT NOT NULL DEFAULT (datetime('now')), session_duration_seconds INTEGER ); CREATE INDEX IF NOT EXISTS idx_player_history_server ON player_history(server_id); CREATE INDEX IF NOT EXISTS idx_player_history_guid ON player_history(guid); CREATE TABLE IF NOT EXISTS bans ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, guid TEXT, name TEXT, reason TEXT, banned_by TEXT, banned_at TEXT NOT NULL DEFAULT (datetime('now')), expires_at TEXT, is_active INTEGER NOT NULL DEFAULT 1, game_data TEXT DEFAULT '{}', CHECK (is_active IN (0, 1)) ); CREATE INDEX IF NOT EXISTS idx_bans_server ON bans(server_id); CREATE INDEX IF NOT EXISTS idx_bans_guid ON bans(guid); CREATE INDEX IF NOT EXISTS idx_bans_active ON bans(is_active); CREATE TABLE IF NOT EXISTS logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, timestamp TEXT NOT NULL, level TEXT NOT NULL DEFAULT 'info', message TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')), CHECK (level IN ('info', 'warning', 'error')) ); CREATE INDEX IF NOT EXISTS idx_logs_server_ts ON logs(server_id, timestamp); CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level); CREATE INDEX IF NOT EXISTS idx_logs_created ON logs(created_at); CREATE TABLE IF NOT EXISTS metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, timestamp TEXT NOT NULL DEFAULT (datetime('now')), cpu_percent REAL, ram_mb REAL, player_count INTEGER ); CREATE INDEX IF NOT EXISTS idx_metrics_server_ts ON metrics(server_id, timestamp); CREATE TABLE IF NOT EXISTS server_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, server_id INTEGER NOT NULL REFERENCES servers(id) ON DELETE CASCADE, event_type TEXT NOT NULL, actor TEXT, detail TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_events_server ON server_events(server_id, created_at)