package storage const schemaSQL = ` PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1, force_password_reset INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, last_login_at TEXT ); CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, access_token_hash TEXT NOT NULL, refresh_token_hash TEXT NOT NULL, access_expires_at TEXT NOT NULL, refresh_expires_at TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, revoked_at TEXT, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_sessions_access ON sessions(access_token_hash); CREATE INDEX IF NOT EXISTS idx_sessions_refresh ON sessions(refresh_token_hash); CREATE TABLE IF NOT EXISTS invites ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, email TEXT NOT NULL, token_hash TEXT NOT NULL UNIQUE, expires_at TEXT NOT NULL, used_at TEXT, created_by TEXT, created_at TEXT NOT NULL, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(created_by) REFERENCES users(id) ON DELETE SET NULL ); CREATE TABLE IF NOT EXISTS password_resets ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, token_hash TEXT NOT NULL UNIQUE, expires_at TEXT NOT NULL, used_at TEXT, created_at TEXT NOT NULL, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS roles ( id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, description TEXT, is_system INTEGER NOT NULL DEFAULT 0, enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS permissions ( id TEXT PRIMARY KEY, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS role_permissions ( role_id TEXT NOT NULL, permission_id TEXT NOT NULL, created_at TEXT NOT NULL, PRIMARY KEY(role_id, permission_id), FOREIGN KEY(role_id) REFERENCES roles(id) ON DELETE CASCADE, FOREIGN KEY(permission_id) REFERENCES permissions(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS user_roles ( user_id TEXT NOT NULL, role_id TEXT NOT NULL, created_at TEXT NOT NULL, PRIMARY KEY(user_id, role_id), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(role_id) REFERENCES roles(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS services ( id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, upstream_url TEXT NOT NULL, route_prefix TEXT NOT NULL UNIQUE, health_path TEXT NOT NULL DEFAULT '/health', upstream_auth_header TEXT, upstream_auth_value TEXT, internal_token TEXT, enabled INTEGER NOT NULL DEFAULT 1, rpm_limit INTEGER, monthly_quota INTEGER, request_timeout_ms INTEGER, last_validation_at TEXT, last_validation_status TEXT, last_validation_message TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS database_connections ( id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, provider TEXT NOT NULL, connection_url TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1, last_validation_at TEXT, last_validation_status TEXT, last_validation_message TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS api_keys ( id TEXT PRIMARY KEY, name TEXT NOT NULL, key_hash TEXT NOT NULL UNIQUE, key_prefix TEXT NOT NULL, plan TEXT NOT NULL, allowed_service_ids TEXT NOT NULL DEFAULT '[]', enabled INTEGER NOT NULL DEFAULT 1, rpm_limit INTEGER, monthly_quota INTEGER, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, last_used_at TEXT ); CREATE TABLE IF NOT EXISTS usage_counters ( id INTEGER PRIMARY KEY AUTOINCREMENT, api_key_id TEXT NOT NULL, service_id TEXT NOT NULL, period_month TEXT NOT NULL, request_count INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL, UNIQUE(api_key_id, service_id, period_month), FOREIGN KEY(api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE, FOREIGN KEY(service_id) REFERENCES services(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS incident_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, service_id TEXT, api_key_id TEXT, code TEXT NOT NULL, message TEXT NOT NULL, severity TEXT NOT NULL DEFAULT 'medium', http_status INTEGER, count INTEGER NOT NULL DEFAULT 1, occurred_at TEXT NOT NULL, FOREIGN KEY(api_key_id) REFERENCES api_keys(id) ON DELETE SET NULL, FOREIGN KEY(service_id) REFERENCES services(id) ON DELETE SET NULL ); CREATE TABLE IF NOT EXISTS metrics_timeseries ( id INTEGER PRIMARY KEY AUTOINCREMENT, metric TEXT NOT NULL, value REAL NOT NULL, labels_json TEXT, occurred_at TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_metrics_metric_time ON metrics_timeseries(metric, occurred_at DESC); CREATE TABLE IF NOT EXISTS umami_sync_cache ( cache_key TEXT PRIMARY KEY, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, actor_user_id TEXT, action TEXT NOT NULL, target_type TEXT, target_id TEXT, payload_json TEXT, occurred_at TEXT NOT NULL, FOREIGN KEY(actor_user_id) REFERENCES users(id) ON DELETE SET NULL ); `