Replace per-device CheckMK URL field with a global, IP-based lookup. The sync job fetches all host configs from CheckMK once per cycle, matches each device by IP address, and updates its status accordingly. Devices not found in CheckMK are reset to 'unknown'. - Add checkmk_enabled / checkmk_api_user settings; toggle in Settings mirrors the Entra ID pattern (fields dim when disabled) - Sync job uses self-scheduling setTimeout so interval changes apply without a server restart; POST /api/checkmk/sync for manual triggers - Status changes and a per-cycle summary are written to the Logbook - Remove checkMkUrl from Device type, form, list view, and detail panel; status badge and CheckMK panel only render when CheckMK is enabled - Booking offline warning suppressed when CheckMK is disabled - Topology status dot color driven purely by device.status
124 lines
3.5 KiB
TypeScript
124 lines
3.5 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 ''");
|
|
|
|
// 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;
|