30 lines
1.2 KiB
JavaScript
30 lines
1.2 KiB
JavaScript
import { readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const DEFAULT = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../config/actions.json');
|
|
const ID_RE = /^[a-z0-9-]+$/;
|
|
const KINDS = new Set(['service_restart', 'guest_power']);
|
|
const OPS = new Set(['start', 'stop', 'shutdown', 'reboot']);
|
|
const TIERS = new Set(['safe', 'risky']);
|
|
|
|
// Loads + validates the action whitelist. `raw` overrides file read (tests).
|
|
export function loadActions(file = DEFAULT, raw) {
|
|
const cfg = raw || JSON.parse(readFileSync(file, 'utf8'));
|
|
const hosts = cfg.hosts || {};
|
|
const byId = new Map();
|
|
for (const a of (cfg.actions || [])) {
|
|
if (!ID_RE.test(a.id || '')) throw new Error(`invalid action id: ${a.id}`);
|
|
if (!KINDS.has(a.kind)) throw new Error(`unknown kind: ${a.kind}`);
|
|
if (!TIERS.has(a.tier)) throw new Error(`invalid tier: ${a.tier}`);
|
|
if (a.kind === 'guest_power' && !OPS.has(a.op)) throw new Error(`invalid op: ${a.op}`);
|
|
if (byId.has(a.id)) throw new Error(`duplicate action id: ${a.id}`);
|
|
byId.set(a.id, a);
|
|
}
|
|
return {
|
|
list: () => [...byId.values()],
|
|
get: (id) => byId.get(id),
|
|
hostIp: (h) => hosts[h]
|
|
};
|
|
}
|