refactor(caddy): remove redundant GhostGrid domain fields, keep only custom routes
caddy_prod_domain and caddy_dev_domain are already handled by the Proxmox deploy process. The Caddy integration is a generic TLS proxy for additional services (Semaphore, Netbox, etc.) — the custom routes list is the sole mechanism.
This commit is contained in:
184
server-db.ts
184
server-db.ts
@ -10,112 +10,114 @@ 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,
|
||||
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,
|
||||
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
|
||||
lastCheckedAt TEXT,
|
||||
checkMkUrl TEXT NOT NULL DEFAULT '',
|
||||
cmkHostname TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
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
|
||||
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,
|
||||
semaphoreSetupTemplateId TEXT NOT NULL DEFAULT '',
|
||||
semaphoreTeardownTemplateId TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
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
|
||||
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 NOT NULL DEFAULT 0,
|
||||
emailSent INTEGER NOT NULL DEFAULT 0,
|
||||
ansibleSetupTriggered INTEGER NOT NULL DEFAULT 0,
|
||||
ansibleTeardownTriggered INTEGER NOT NULL DEFAULT 0,
|
||||
ansibleSetupJobId TEXT NOT NULL DEFAULT '',
|
||||
ansibleTeardownJobId TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
id TEXT PRIMARY KEY,
|
||||
timestamp TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
deviceId TEXT,
|
||||
userId TEXT
|
||||
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,
|
||||
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
|
||||
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,
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS caddy (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
hostname TEXT NOT NULL,
|
||||
upstream TEXT NOT NULL,
|
||||
tls INTEGER NOT NULL DEFAULT 1,
|
||||
compress INTEGER NOT NULL DEFAULT 1,
|
||||
created_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}`);
|
||||
}
|
||||
}
|
||||
// Seed default settings — INSERT OR IGNORE writes a key only if it is absent.
|
||||
const DEFAULT_SETTINGS: Record<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',
|
||||
semaphore_enabled: 'false',
|
||||
semaphore_api_url: '',
|
||||
semaphore_api_token: '',
|
||||
semaphore_project_id: '',
|
||||
caddy_enabled: 'false',
|
||||
caddy_admin_url: 'http://localhost:2019',
|
||||
};
|
||||
|
||||
ensureColumn('devices', 'checkMkUrl', "checkMkUrl TEXT NOT NULL DEFAULT ''");
|
||||
ensureColumn('devices', 'cmkHostname', "cmkHostname TEXT NOT NULL DEFAULT ''");
|
||||
ensureColumn('labs', 'semaphoreSetupTemplateId', "semaphoreSetupTemplateId TEXT NOT NULL DEFAULT ''");
|
||||
ensureColumn('labs', 'semaphoreTeardownTemplateId', "semaphoreTeardownTemplateId TEXT NOT NULL DEFAULT ''");
|
||||
ensureColumn('bookings', 'ansibleSetupTriggered', 'ansibleSetupTriggered INTEGER NOT NULL DEFAULT 0');
|
||||
ensureColumn('bookings', 'ansibleTeardownTriggered', 'ansibleTeardownTriggered INTEGER NOT NULL DEFAULT 0');
|
||||
ensureColumn('bookings', 'ansibleSetupJobId', "ansibleSetupJobId TEXT NOT NULL DEFAULT ''");
|
||||
ensureColumn('bookings', 'ansibleTeardownJobId', "ansibleTeardownJobId 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'],
|
||||
['semaphore_enabled', 'false'],
|
||||
['semaphore_api_url', ''],
|
||||
['semaphore_api_token', ''],
|
||||
['semaphore_project_id', ''],
|
||||
];
|
||||
for (const [k, v] of _defaultSettings) _insertDefault.run(k, v);
|
||||
const seedSetting = db.prepare('INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)');
|
||||
for (const [key, value] of Object.entries(DEFAULT_SETTINGS)) seedSetting.run(key, value);
|
||||
|
||||
export function getSetting(key: string): string {
|
||||
const row = db.prepare('SELECT value FROM settings WHERE key = ?').get(key) as { value: string } | undefined;
|
||||
@ -131,4 +133,28 @@ export function getAllSettings(): Record<string, string> {
|
||||
return Object.fromEntries(rows.map(r => [r.key, r.value]));
|
||||
}
|
||||
|
||||
export interface CaddyRoute {
|
||||
id: number;
|
||||
hostname: string;
|
||||
upstream: string;
|
||||
tls: number;
|
||||
compress: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export function getCaddyRoutes(): CaddyRoute[] {
|
||||
return db.prepare('SELECT * FROM caddy ORDER BY id ASC').all() as CaddyRoute[];
|
||||
}
|
||||
|
||||
export function addCaddyRoute(hostname: string, upstream: string, tls: boolean, compress: boolean): CaddyRoute {
|
||||
const { lastInsertRowid } = db.prepare(
|
||||
'INSERT INTO caddy (hostname, upstream, tls, compress) VALUES (?, ?, ?, ?)'
|
||||
).run(hostname, upstream, tls ? 1 : 0, compress ? 1 : 0);
|
||||
return db.prepare('SELECT * FROM caddy WHERE id = ?').get(lastInsertRowid) as CaddyRoute;
|
||||
}
|
||||
|
||||
export function deleteCaddyRoute(id: number): void {
|
||||
db.prepare('DELETE FROM caddy WHERE id = ?').run(id);
|
||||
}
|
||||
|
||||
export default db;
|
||||
|
||||
Reference in New Issue
Block a user