diff --git a/src/db/db.ts b/src/db/db.ts index 44eb5a4..770f597 100644 --- a/src/db/db.ts +++ b/src/db/db.ts @@ -4,53 +4,80 @@ import { Database } from "bun:sqlite"; mkdirSync("./data", { recursive: true }); const db = new Database("./data/mydb.sqlite", { create: true }); -if (!db.query("SELECT * FROM sqlite_master WHERE type='table'").get()) { +// Always keep foreign keys on (SQLite defaults to off). +db.exec("PRAGMA foreign_keys = ON;"); + +// ----------------------------------------------------------------------------- +// Initial schema bootstrap (fresh install) +// ----------------------------------------------------------------------------- +const hasAnyTable = db.query("SELECT 1 FROM sqlite_master WHERE type='table' LIMIT 1").get(); +if (!hasAnyTable) { db.exec(` CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - email TEXT NOT NULL, - password TEXT NOT NULL + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'user' ); + +CREATE TABLE IF NOT EXISTS jobs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + date_created TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'queued', + num_files INTEGER NOT NULL DEFAULT 0, + finished_files INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS file_names ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id INTEGER NOT NULL, file_name TEXT NOT NULL, output_file_name TEXT NOT NULL, - status TEXT DEFAULT 'not started', - FOREIGN KEY (job_id) REFERENCES jobs(id) + status TEXT NOT NULL DEFAULT 'queued', + FOREIGN KEY (job_id) REFERENCES jobs(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS jobs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, - date_created TEXT NOT NULL, - status TEXT DEFAULT 'not started', - num_files INTEGER DEFAULT 0, - FOREIGN KEY (user_id) REFERENCES users(id) -); -PRAGMA user_version = 1;`); + +PRAGMA user_version = 1; +`); } -const dbVersion = (db.query("PRAGMA user_version").get() as { user_version?: number }).user_version; +// ----------------------------------------------------------------------------- +// Version marker (kept for backwards-compatibility; schema is enforced below). +// ----------------------------------------------------------------------------- +const dbVersion = (db.query("PRAGMA user_version").get() as { user_version?: number }) + .user_version; -// v0 -> v1 migration: ensure file_names.status exists (older DBs didn't have it) if ((dbVersion ?? 0) === 0) { - const fileCols = db.query("PRAGMA table_info(file_names)").all() as { name: string }[]; - if (!fileCols.some((col) => col.name === "status")) { - // keep default aligned with current code paths that assume a non-null status - db.exec("ALTER TABLE file_names ADD COLUMN status TEXT NOT NULL DEFAULT 'queued';"); - } db.exec("PRAGMA user_version = 1;"); console.log("Updated database to version 1."); } +// ----------------------------------------------------------------------------- +// One-time migration: Policy A +// If the `role` column is newly added AND there are existing users, +// make the oldest user (lowest id) an admin. +// ----------------------------------------------------------------------------- const userColumns = db.query("PRAGMA table_info(users)").all() as { name: string }[]; const hasRoleColumn = userColumns.some((col) => col.name === "role"); + if (!hasRoleColumn) { db.exec("ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'user';"); - console.log("Added 'role' column to users table."); + + const oldest = db + .query("SELECT id FROM users ORDER BY id ASC LIMIT 1") + .get() as { id: number } | null; + + if (oldest) { + db.query("UPDATE users SET role = 'admin' WHERE id = ?").run(oldest.id); + console.log("Added 'role' column; promoted oldest existing user to admin (Policy A)."); + } else { + console.log("Added 'role' column to users table (no users to promote)."); + } } -// enable WAL mode +// enable WAL mode (better concurrency for Bun + SQLite) db.exec("PRAGMA journal_mode = WAL;"); export default db;