const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); const DB_PATH = path.join(__dirname, '..', 'data', 'access-manager.db'); fs.mkdirSync(path.dirname(DB_PATH), { recursive: true }); const db = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); // ─── Schema ─── db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE NOT NULL COLLATE NOCASE, name TEXT NOT NULL, password_hash TEXT, auth_methods TEXT NOT NULL DEFAULT '["google"]', metadata TEXT DEFAULT '{}', tags TEXT DEFAULT '[]', created_at TEXT NOT NULL DEFAULT (datetime('now')), created_by TEXT DEFAULT 'system', last_login TEXT, status TEXT NOT NULL DEFAULT 'active' ); CREATE TABLE IF NOT EXISTS roles ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, display_name TEXT NOT NULL, description TEXT, is_system INTEGER NOT NULL DEFAULT 0, priority INTEGER NOT NULL DEFAULT 100, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, app TEXT NOT NULL, feature TEXT NOT NULL, action TEXT NOT NULL, display_name TEXT, description TEXT, category TEXT DEFAULT 'general', created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(app, feature, action) ); CREATE TABLE IF NOT EXISTS role_permissions ( role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE, permission_id INTEGER NOT NULL REFERENCES permissions(id) ON DELETE CASCADE, PRIMARY KEY (role_id, permission_id) ); CREATE TABLE IF NOT EXISTS user_roles ( user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE, scope TEXT NOT NULL DEFAULT '*', granted_at TEXT NOT NULL DEFAULT (datetime('now')), granted_by TEXT, PRIMARY KEY (user_id, role_id, scope) ); CREATE TABLE IF NOT EXISTS services ( id TEXT PRIMARY KEY, display_name TEXT NOT NULL, hostname TEXT, hostnames TEXT DEFAULT '[]', port INTEGER, status TEXT NOT NULL DEFAULT 'active', features_registered INTEGER DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL DEFAULT (datetime('now')), actor_email TEXT NOT NULL, action TEXT NOT NULL, target_type TEXT, target_id TEXT, detail TEXT, ip TEXT, service TEXT ); CREATE INDEX IF NOT EXISTS idx_user_roles_user ON user_roles(user_id); CREATE INDEX IF NOT EXISTS idx_user_roles_role ON user_roles(role_id); CREATE INDEX IF NOT EXISTS idx_role_perms_role ON role_permissions(role_id); CREATE INDEX IF NOT EXISTS idx_permissions_app ON permissions(app); CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_log(actor_email); CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp); `); module.exports = db;