import type sqlite3 from "better-sqlite3"; import { config } from "../../config"; import { logger } from "../../logger"; import { migrations } from "./migrations"; export const DATABASE_VERSION = 3; let database: sqlite3.Database | undefined; let log = logger.child({ module: "database" }); export function getDatabase(): sqlite3.Database { if (!database) { throw new Error("Sqlite database not initialized."); } return database; } export async function initializeDatabase() { if (!config.eventLogging) { return; } log.info("Initializing database..."); const sqlite3 = await import("better-sqlite3"); database = sqlite3.default(config.sqliteDataPath); migrateDatabase(); database.pragma("journal_mode = WAL"); log.info("Database initialized."); } export function migrateDatabase( targetVersion = DATABASE_VERSION, targetDb?: sqlite3.Database ) { const db = targetDb || getDatabase(); const currentVersion = db.pragma("user_version", { simple: true }); assertNumber(currentVersion); if (currentVersion === targetVersion) { log.info("No migrations to run."); return; } const direction = currentVersion < targetVersion ? "up" : "down"; const pending = migrations .slice() .sort((a, b) => direction === "up" ? a.version - b.version : b.version - a.version ) .filter((m) => direction === "up" ? m.version > currentVersion && m.version <= targetVersion : m.version > targetVersion && m.version <= currentVersion ); if (pending.length === 0) { log.warn("No pending migrations found."); return; } for (const migration of pending) { const { version, name, up, down } = migration; if ( (direction === "up" && version > currentVersion) || (direction === "down" && version <= currentVersion) ) { if (direction === "up") { log.info({ name }, "Applying migration."); up(db); db.pragma("user_version = " + version); } else { log.info({ name }, "Reverting migration."); down(db); db.pragma("user_version = " + (version - 1)); } } } log.info("Migrations applied."); } function assertNumber(value: unknown): asserts value is number { if (typeof value !== "number") { throw new Error("Expected number"); } } export { EventLogEntry } from "./repos/event";