import fs from 'fs'; import path from 'path'; import { DatabaseSync } from 'node:sqlite'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const dbPath = process.env.SQLITE_PATH || path.join(__dirname, '..', 'data', 'crm.db'); fs.mkdirSync(path.dirname(dbPath), { recursive: true }); const db = new DatabaseSync(dbPath); db.exec('PRAGMA foreign_keys = ON'); const machineCols = db.prepare('PRAGMA table_info(machines)').all(); if (!machineCols.some((c) => c.name === 'extras')) { db.exec('ALTER TABLE machines ADD COLUMN extras TEXT'); } const hasCustomerId = machineCols.some((c) => c.name === 'customer_id'); const tables = db .prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'", ) .all() .map((r) => r.name); const hasCustomersTable = tables.includes('customers'); const eventCols = db.prepare('PRAGMA table_info(events)').all(); if (eventCols.length > 0 && !eventCols.some((c) => c.name === 'remote_duration_seconds')) { db.exec('ALTER TABLE events ADD COLUMN remote_duration_seconds INTEGER'); } const hasEventExtras = eventCols.some((c) => c.name === 'callback_number'); if (eventCols.length > 0 && !hasEventExtras) { db.exec('BEGIN'); try { db.exec(` CREATE TABLE events_new ( "id" TEXT NOT NULL PRIMARY KEY, "ticket_id" TEXT NOT NULL, "type" TEXT NOT NULL CHECK ("type" IN ('NOTE', 'CALL', 'REMOTE', 'PART', 'SYSTEM')), "description" TEXT NOT NULL, "callback_number" TEXT, "teamviewer_id" TEXT, "article_number" TEXT, "remote_duration_seconds" INTEGER, "created_at" TEXT NOT NULL DEFAULT (datetime('now')), FOREIGN KEY ("ticket_id") REFERENCES "tickets" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); INSERT INTO events_new (id, ticket_id, type, description, callback_number, teamviewer_id, article_number, remote_duration_seconds, created_at) SELECT id, ticket_id, CASE WHEN type = 'WORK' THEN 'REMOTE' ELSE type END, description, NULL, NULL, NULL, NULL, created_at FROM events; DROP TABLE events; ALTER TABLE events_new RENAME TO events; `); db.exec( 'CREATE INDEX IF NOT EXISTS events_ticket_id_idx ON "events" ("ticket_id")', ); db.exec( 'CREATE INDEX IF NOT EXISTS events_created_at_idx ON "events" ("created_at")', ); db.exec('COMMIT'); } catch (e) { db.exec('ROLLBACK'); throw e; } } if (hasCustomerId || hasCustomersTable) { db.exec('BEGIN'); try { db.exec(` CREATE TABLE machines_new ( "id" TEXT NOT NULL PRIMARY KEY, "name" TEXT NOT NULL, "typ" TEXT NOT NULL, "seriennummer" TEXT NOT NULL, "standort" TEXT NOT NULL, "extras" TEXT, "created_at" TEXT NOT NULL DEFAULT (datetime('now')), "updated_at" TEXT NOT NULL DEFAULT (datetime('now')) ); INSERT INTO machines_new (id, name, typ, seriennummer, standort, extras, created_at, updated_at) SELECT id, name, typ, seriennummer, standort, extras, created_at, updated_at FROM machines; DROP TABLE machines; ALTER TABLE machines_new RENAME TO machines; `); if (hasCustomersTable) { db.exec('DROP TABLE customers'); } db.exec('COMMIT'); } catch (e) { db.exec('ROLLBACK'); throw e; } } const tbl = db .prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name='users'", ) .get(); if (!tbl) { db.exec(` CREATE TABLE "users" ( "id" TEXT NOT NULL PRIMARY KEY, "username" TEXT NOT NULL UNIQUE, "password_hash" TEXT, "role" TEXT NOT NULL DEFAULT 'user' CHECK ("role" IN ('admin', 'user')), "source" TEXT NOT NULL DEFAULT 'local' CHECK ("source" IN ('local', 'ldap')), "ldap_dn" TEXT, "active" INTEGER NOT NULL DEFAULT 1 CHECK ("active" IN (0, 1)), "created_at" TEXT NOT NULL DEFAULT (datetime('now')), "updated_at" TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS users_username_idx ON "users" ("username"); `); } const tblSet = db .prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name='app_settings'", ) .get(); if (!tblSet) { db.exec(` CREATE TABLE "app_settings" ( "key" TEXT NOT NULL PRIMARY KEY, "value" TEXT NOT NULL ); `); } const ldapLogTbl = db .prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name='ldap_sync_log'", ) .get(); if (!ldapLogTbl) { db.exec(` CREATE TABLE "ldap_sync_log" ( "id" TEXT NOT NULL PRIMARY KEY, "started_at" TEXT NOT NULL, "finished_at" TEXT NOT NULL, "trigger_type" TEXT NOT NULL CHECK ("trigger_type" IN ('manual', 'automatic')), "status" TEXT NOT NULL CHECK ("status" IN ('success', 'error')), "users_synced" INTEGER NOT NULL DEFAULT 0, "error_message" TEXT ); CREATE INDEX ldap_sync_log_finished_idx ON "ldap_sync_log" ("finished_at" DESC); `); } export default db;