import Database from 'better-sqlite3'; import { drizzle } from 'drizzle-orm/better-sqlite3'; import { app } from 'electron'; import path from 'node:path'; import * as schema from './schema'; // SQL to create all tables if they don't exist (non-destructive push strategy) const MIGRATION_SQL = ` CREATE TABLE IF NOT EXISTS clients ( id TEXT PRIMARY KEY, parent_id TEXT, name TEXT NOT NULL, industry TEXT, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS projects ( id TEXT PRIMARY KEY, client_id TEXT, name TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'active', ai_summary TEXT, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY, project_id TEXT, title TEXT NOT NULL, description TEXT, status TEXT NOT NULL DEFAULT 'todo', priority TEXT NOT NULL DEFAULT 'medium', assignee TEXT, due_date INTEGER, is_ai_suggested INTEGER NOT NULL DEFAULT 0, is_approved INTEGER NOT NULL DEFAULT 1, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS checkpoints ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL, title TEXT NOT NULL, date INTEGER NOT NULL, is_ai_suggested INTEGER NOT NULL DEFAULT 0, is_approved INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS notes ( id TEXT PRIMARY KEY, project_id TEXT, title TEXT NOT NULL, content TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS task_comments ( id TEXT PRIMARY KEY, task_id TEXT NOT NULL, author TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL ); `; type DbInstance = ReturnType>; let dbInstance: DbInstance | null = null; export function initDb(): DbInstance { const userDataPath = app.getPath('userData'); const dbPath = path.join(userDataPath, 'adiuva.db'); const sqlite = new Database(dbPath); // Enable WAL mode for better concurrent read performance sqlite.pragma('journal_mode = WAL'); // Run non-destructive migrations on every start sqlite.exec(MIGRATION_SQL); // Additive column migrations (SQLite has no ADD COLUMN IF NOT EXISTS) try { sqlite.exec('ALTER TABLE tasks ADD COLUMN is_ai_suggested INTEGER NOT NULL DEFAULT 0'); } catch { /* already exists */ } try { sqlite.exec('ALTER TABLE tasks ADD COLUMN is_approved INTEGER NOT NULL DEFAULT 1'); } catch { /* already exists */ } dbInstance = drizzle(sqlite, { schema }); return dbInstance; } export function getDb(): DbInstance { if (!dbInstance) { throw new Error('Database not initialized. Call initDb() first.'); } return dbInstance; }