CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE TABLE IF NOT EXISTS agents ( id TEXT PRIMARY KEY, name TEXT NOT NULL, hostname TEXT, platform TEXT NOT NULL DEFAULT 'linux', token_prefix TEXT NOT NULL, token_hash TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', policy_mode TEXT NOT NULL DEFAULT 'blacklist', policy_generation INTEGER NOT NULL DEFAULT 1, last_seen_at TEXT, last_seen_ip TEXT, last_apply_at TEXT, last_apply_status TEXT, last_apply_error TEXT, last_apply_prefix_count INTEGER DEFAULT 0, last_apply_packets_dropped INTEGER NOT NULL DEFAULT 0, last_apply_packets_accepted INTEGER NOT NULL DEFAULT 0, last_apply_kernel_method TEXT, client_version TEXT, settings_json TEXT NOT NULL DEFAULT '{}', created_by_user_id TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), approved_at TEXT, revoked_at TEXT, CHECK (status IN ('pending', 'approved', 'revoked')), CHECK (platform IN ('linux', 'mikrotik')), CHECK (policy_mode IN ('blacklist', 'whitelist')), CHECK (length(trim(name)) > 0) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_token_hash ON agents (token_hash); CREATE INDEX IF NOT EXISTS idx_agents_status ON agents (status); CREATE TABLE IF NOT EXISTS ip_lists ( id TEXT PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL, config_json TEXT NOT NULL DEFAULT '{}', content_hash TEXT, refreshed_at TEXT, last_error TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), CHECK (type IN ('static', 'json_url', 'domains', 'evobgp_community')) ); CREATE TABLE IF NOT EXISTS ip_list_entries ( id TEXT PRIMARY KEY, list_id TEXT NOT NULL REFERENCES ip_lists (id) ON DELETE CASCADE, cidr TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_ip_list_entries_list_cidr ON ip_list_entries (list_id, cidr); CREATE TABLE IF NOT EXISTS policy_rules ( id TEXT PRIMARY KEY, agent_id TEXT REFERENCES agents (id) ON DELETE CASCADE, priority INTEGER NOT NULL, action TEXT NOT NULL, list_id TEXT REFERENCES ip_lists (id) ON DELETE CASCADE, cidr TEXT, comment TEXT, created_by_user_id TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), CHECK (action IN ('allow', 'deny')), CHECK (priority >= 1 AND priority <= 10000) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_policy_rules_agent_priority ON policy_rules (agent_id, priority); CREATE TABLE IF NOT EXISTS ip_overrides ( id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents (id) ON DELETE CASCADE, cidr TEXT NOT NULL, action TEXT NOT NULL, comment TEXT, created_by_user_id TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), CHECK (action IN ('allow', 'deny')) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_ip_overrides_agent_cidr ON ip_overrides (agent_id, cidr); CREATE TABLE IF NOT EXISTS agent_stats_samples ( id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents (id) ON DELETE CASCADE, packets_dropped INTEGER NOT NULL DEFAULT 0, packets_accepted INTEGER NOT NULL DEFAULT 0, prefix_count INTEGER NOT NULL DEFAULT 0, kernel_method TEXT, recorded_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE INDEX IF NOT EXISTS idx_agent_stats_agent_time ON agent_stats_samples (agent_id, recorded_at); INSERT OR IGNORE INTO settings (key, value) VALUES ('enroll_seed', ''); INSERT OR IGNORE INTO settings (key, value) VALUES ('evobgp_api_url', ''); INSERT OR IGNORE INTO settings (key, value) VALUES ('evobgp_api_token', ''); INSERT OR IGNORE INTO settings (key, value) VALUES ('list_refresh_cron', '*/5 * * * *'); INSERT OR IGNORE INTO settings (key, value) VALUES ('agent_sync_interval_sec', '60');