58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
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;
|