import Database from 'better-sqlite3'; import { mkdirSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; const dbPath = resolve(process.env.DB_PATH ?? './data/guestbook.db'); // Ensure directory exists mkdirSync(dirname(dbPath), { recursive: true }); const db = new Database(dbPath); // WAL mode improves concurrent read performance db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); db.exec(` CREATE TABLE IF NOT EXISTS guestbook_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, display_name TEXT NOT NULL, message TEXT NOT NULL, website TEXT, ip_hash TEXT, status TEXT NOT NULL DEFAULT 'pending', created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), moderated_at TEXT, moderation_note TEXT ); CREATE TABLE IF NOT EXISTS admin_sessions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), expires_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS rate_limit ( id INTEGER PRIMARY KEY AUTOINCREMENT, ip_hash TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) ); CREATE TABLE IF NOT EXISTS audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, action TEXT NOT NULL, entry_id INTEGER, admin_session TEXT, note TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) ); CREATE INDEX IF NOT EXISTS idx_entries_status ON guestbook_entries(status); CREATE INDEX IF NOT EXISTS idx_entries_created ON guestbook_entries(created_at); CREATE INDEX IF NOT EXISTS idx_rate_limit_ip ON rate_limit(ip_hash, created_at); CREATE INDEX IF NOT EXISTS idx_sessions_expires ON admin_sessions(expires_at); `); export default db;