Files
ProdDash/tools/alert.js
T

84 lines
3.5 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
'use strict';
/**
* Raise, clear or list ProdDash alerts from the command line — for trying a
* dashboard's channels and the admin colours, or from a script on the
* machine running ProdDash (anything that can POST JSON can raise one).
*
* node tools/alert.js "Service over by 5:00" # low, channel 1, 15 s
* node tools/alert.js -c 2 -l critical -d 0 "LTC lost" # held until cleared
* node tools/alert.js -c 2 --clear
* node tools/alert.js --list
*
* Options: -c/--channel 1–6 (default 1), -l/--level low|medium|critical
* (default low), -d/--duration seconds, 0 to hold until cleared (default the
* server's 15), --url http://host:port (default http://127.0.0.1:24500, or
* $PRODDASH_URL), --clear, --list. Node 18+, no dependencies.
*/
const args = process.argv.slice(2);
const opts = { channel: 1, level: 'low', duration: undefined, url: process.env.PRODDASH_URL || 'http://127.0.0.1:24500', clear: false, list: false };
const words = [];
for (let i = 0; i < args.length; i += 1) {
const a = args[i];
const next = () => args[++i];
if (a === '-c' || a === '--channel') opts.channel = Number(next());
else if (a === '-l' || a === '--level') opts.level = String(next() || '');
else if (a === '-d' || a === '--duration') opts.duration = Number(next());
else if (a === '--url') opts.url = String(next() || opts.url);
else if (a === '--clear') opts.clear = true;
else if (a === '--list') opts.list = true;
else if (a === '-h' || a === '--help') {
console.log('usage: node tools/alert.js [-c 1-6] [-l low|medium|critical] [-d seconds] [--url http://host:port] "text"\n'
+ ' node tools/alert.js -c <n> --clear\n'
+ ' node tools/alert.js --list');
process.exit(0);
} else words.push(a);
}
const base = opts.url.replace(/\/+$/, '');
async function call(path, init) {
const res = await fetch(base + path, init);
const body = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`);
return body;
}
(async () => {
if (opts.list) {
const body = await call('/api/alerts');
const active = body.active || [];
console.log(`${active.length} active alert${active.length === 1 ? '' : 's'}; colours:`,
Object.entries(body.colors || {}).map(([k, v]) => `${k} ${v.join('→')}`).join(', '));
for (const a of active) {
console.log(` channel ${a.channel} ${a.level.padEnd(8)} "${a.text}" from ${a.source}`
+ (a.remainingMs === null ? ' (until cleared)' : ` (${Math.ceil(a.remainingMs / 1000)} s left)`));
}
return;
}
if (opts.clear) {
const body = await call(`/api/alerts/${opts.channel}`, { method: 'DELETE' });
console.log(body.cleared ? `cleared channel ${opts.channel}` : `nothing was on channel ${opts.channel}`);
return;
}
const text = words.join(' ').trim();
if (!text) {
console.error('say what happened: node tools/alert.js -c 1 -l critical "LTC lost"');
process.exit(2);
}
const payload = { channel: opts.channel, level: opts.level, text, source: 'cli' };
if (opts.duration !== undefined) payload.duration = opts.duration;
const body = await call('/api/alerts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const a = body.alert;
console.log(`raised ${a.level} on channel ${a.channel}: "${a.text}"`
+ (a.remainingMs === null ? ' (until cleared)' : ` (${a.duration} s)`));
})().catch((err) => {
console.error(err.message || String(err));
process.exit(1);
});