feat: log login events in logbook; improve CheckMK error reporting
Successful logins now write a system-type logbook entry. CheckMK sync reports configuration errors, host-fetch failures, and per-device sync failures as logbook entries instead of silently dropping them; inherits effective_attributes IP fallback.
This commit is contained in:
45
server.ts
45
server.ts
@ -125,6 +125,10 @@ async function startServer() {
|
||||
const token = jwt.sign({ userId: row.id, email: row.email }, JWT_SECRET, { expiresIn: JWT_EXPIRY });
|
||||
const user: User = { id: row.id, name: row.name, role: row.role, email: row.email };
|
||||
|
||||
const logId = uid("log");
|
||||
db.prepare(`INSERT INTO logs (id, timestamp, type, message, deviceId, userId) VALUES (?, ?, ?, ?, ?, ?)`)
|
||||
.run(logId, new Date().toISOString(), 'system', `${row.name} logged in.`, null, row.id);
|
||||
|
||||
res.json({ token, user });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: err.message });
|
||||
@ -686,36 +690,51 @@ async function startServer() {
|
||||
// in Settings take effect on the next cycle without a server restart.
|
||||
// -------------------------------------------------------------
|
||||
async function syncCheckMkStatuses() {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
if (getSetting('checkmk_enabled') !== 'true') return;
|
||||
|
||||
const CHECKMK_API_URL = getSetting('checkmk_api_url') || process.env.CHECKMK_API_URL;
|
||||
const CHECKMK_API_USER = getSetting('checkmk_api_user') || process.env.CHECKMK_API_USER || 'automation';
|
||||
const CHECKMK_API_SECRET = getSetting('checkmk_api_secret') || process.env.CHECKMK_API_SECRET;
|
||||
if (!CHECKMK_API_URL || !CHECKMK_API_SECRET) return;
|
||||
|
||||
if (!CHECKMK_API_URL || !CHECKMK_API_SECRET) {
|
||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
||||
.run(uid('log'), now, 'system', 'CheckMK sync skipped — API URL or secret not configured. Check Settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
const authHeader = `Bearer ${CHECKMK_API_USER} ${CHECKMK_API_SECRET}`;
|
||||
|
||||
// Step 1: build IP → hostname map from CheckMK host configurations
|
||||
// Falls back to effective_attributes for IPs inherited from a parent folder.
|
||||
let ipToHostname: Map<string, string>;
|
||||
try {
|
||||
const cfgRes = await fetch(
|
||||
`${CHECKMK_API_URL}/domain-types/host_config/collections/all`,
|
||||
{ headers: { Authorization: `Bearer ${CHECKMK_API_USER} ${CHECKMK_API_SECRET}`, Accept: 'application/json' } }
|
||||
{ headers: { Authorization: authHeader, Accept: 'application/json' } }
|
||||
);
|
||||
if (!cfgRes.ok) throw new Error(`HTTP ${cfgRes.status}`);
|
||||
if (!cfgRes.ok) throw new Error(`HTTP ${cfgRes.status} — ${await cfgRes.text()}`);
|
||||
const cfgData = await cfgRes.json();
|
||||
ipToHostname = new Map<string, string>();
|
||||
for (const host of cfgData?.value ?? []) {
|
||||
const ip: string | undefined = host?.extensions?.attributes?.ipaddress;
|
||||
const ext = host?.extensions;
|
||||
const ip: string | undefined =
|
||||
ext?.attributes?.ipaddress || ext?.effective_attributes?.ipaddress;
|
||||
const name: string | undefined = host?.id;
|
||||
if (ip && name) ipToHostname.set(ip, name);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[CheckMK] Failed to fetch host configs:', err);
|
||||
} catch (err: any) {
|
||||
const msg = `CheckMK sync failed — could not fetch host list: ${err?.message ?? err}`;
|
||||
console.error('[CheckMK]', msg);
|
||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
||||
.run(uid('log'), now, 'system', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: update each device based on IP lookup and log status changes
|
||||
const rows = db.prepare('SELECT id, hostname, ip, status FROM devices').all() as { id: string; hostname: string; ip: string; status: string }[];
|
||||
const counts = { online: 0, offline: 0, unknown: 0 };
|
||||
const now = new Date().toISOString();
|
||||
|
||||
for (const dev of rows) {
|
||||
const cmkHost = ipToHostname.get(dev.ip);
|
||||
@ -731,9 +750,9 @@ async function startServer() {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${CHECKMK_API_URL}/objects/host/${encodeURIComponent(cmkHost)}`,
|
||||
{ headers: { Authorization: `Bearer ${CHECKMK_API_USER} ${CHECKMK_API_SECRET}`, Accept: 'application/json' } }
|
||||
{ headers: { Authorization: authHeader, Accept: 'application/json' } }
|
||||
);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status} — ${await res.text()}`);
|
||||
const data = await res.json();
|
||||
const hardState: number = data?.extensions?.state ?? -1;
|
||||
const newStatus = hardState === 0 ? 'online' : 'offline';
|
||||
@ -743,13 +762,15 @@ async function startServer() {
|
||||
.run(uid('log'), now, 'status', `CheckMK: ${dev.hostname} (${dev.ip}) status changed to ${newStatus} (was: ${dev.status}).`, dev.id);
|
||||
}
|
||||
counts[newStatus as 'online' | 'offline']++;
|
||||
} catch (err) {
|
||||
console.error(`[CheckMK] Status sync failed for ${dev.hostname} (${dev.ip}):`, err);
|
||||
} catch (err: any) {
|
||||
const msg = `CheckMK: status sync failed for ${dev.hostname} (${dev.ip}) — ${err?.message ?? err}`;
|
||||
console.error('[CheckMK]', msg);
|
||||
db.prepare('INSERT INTO logs (id, timestamp, type, message, deviceId) VALUES (?, ?, ?, ?, ?)')
|
||||
.run(uid('log'), now, 'system', msg, dev.id);
|
||||
counts.unknown++;
|
||||
}
|
||||
}
|
||||
|
||||
// Summary log entry for every sync run
|
||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
||||
.run(uid('log'), now, 'system',
|
||||
`CheckMK sync completed — ${counts.online} online, ${counts.offline} offline, ${counts.unknown} unknown (${rows.length} devices total).`);
|
||||
|
||||
Reference in New Issue
Block a user