125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
import path from 'path';
|
|
|
|
const DB_FILE = path.join(process.cwd(), 'ghostgrid.db');
|
|
|
|
console.log(`[Database] Connecting to SQLite database at: ${DB_FILE}`);
|
|
const db = new Database(DB_FILE);
|
|
|
|
db.pragma('journal_mode = WAL');
|
|
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'User',
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS devices (
|
|
id TEXT PRIMARY KEY,
|
|
hostname TEXT NOT NULL,
|
|
ip TEXT NOT NULL,
|
|
location TEXT NOT NULL,
|
|
notes TEXT,
|
|
type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
emergencySheet TEXT NOT NULL,
|
|
lastCheckedAt TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS labs (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
contactPerson TEXT NOT NULL,
|
|
location TEXT NOT NULL,
|
|
deviceIds TEXT NOT NULL,
|
|
topology TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS bookings (
|
|
id TEXT PRIMARY KEY,
|
|
labId TEXT NOT NULL,
|
|
userId TEXT NOT NULL,
|
|
startDateTime TEXT NOT NULL,
|
|
endDateTime TEXT NOT NULL,
|
|
notes TEXT,
|
|
status TEXT NOT NULL,
|
|
notified INTEGER DEFAULT 0,
|
|
emailSent INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS logs (
|
|
id TEXT PRIMARY KEY,
|
|
timestamp TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
message TEXT NOT NULL,
|
|
deviceId TEXT,
|
|
userId TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS links (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
category TEXT NOT NULL DEFAULT '',
|
|
color TEXT NOT NULL DEFAULT 'emerald',
|
|
createdBy TEXT,
|
|
createdAt TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
`);
|
|
|
|
// Lightweight migrations for columns added after the initial release.
|
|
// CREATE TABLE IF NOT EXISTS never alters an existing table, so add them by hand.
|
|
function ensureColumn(table: string, column: string, ddl: string) {
|
|
const cols = db.prepare(`PRAGMA table_info(${table})`).all() as { name: string }[];
|
|
if (!cols.some(c => c.name === column)) {
|
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
|
|
}
|
|
}
|
|
|
|
ensureColumn('devices', 'checkMkUrl', "checkMkUrl TEXT NOT NULL DEFAULT ''");
|
|
ensureColumn('devices', 'cmkHostname', "cmkHostname TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Seed default settings (INSERT OR IGNORE = only if key absent)
|
|
const _insertDefault = db.prepare('INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)');
|
|
const _defaultSettings: [string, string][] = [
|
|
['azure_enabled', 'false'],
|
|
['azure_client_id', ''],
|
|
['azure_tenant_id', ''],
|
|
['azure_client_secret', ''],
|
|
['azure_redirect_uri', ''],
|
|
['azure_allowed_group', ''],
|
|
['checkmk_enabled', 'false'],
|
|
['checkmk_api_url', ''],
|
|
['checkmk_api_user', 'automation'],
|
|
['checkmk_api_secret', ''],
|
|
['checkmk_sync_interval_ms', '60000'],
|
|
];
|
|
for (const [k, v] of _defaultSettings) _insertDefault.run(k, v);
|
|
|
|
export function getSetting(key: string): string {
|
|
const row = db.prepare('SELECT value FROM settings WHERE key = ?').get(key) as { value: string } | undefined;
|
|
return row?.value ?? '';
|
|
}
|
|
|
|
export function setSetting(key: string, value: string): void {
|
|
db.prepare("INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES (?, ?, datetime('now'))").run(key, value);
|
|
}
|
|
|
|
export function getAllSettings(): Record<string, string> {
|
|
const rows = db.prepare('SELECT key, value FROM settings').all() as { key: string; value: string }[];
|
|
return Object.fromEntries(rows.map(r => [r.key, r.value]));
|
|
}
|
|
|
|
export default db;
|