Files
broccolini-bot/services/notificationEnabled.js

103 lines
3.0 KiB
JavaScript

/**
* Canonical enable/disable state accessor for per-alert notifications.
*
* State lives in two CONFIG keys:
* - NOTIFICATIONS_MASTER_ENABLED (boolean) — global kill switch
* - NOTIFICATION_ENABLED_JSON (JSON string → flat { [key]: boolean })
*
* Defaults: master off, every key off. Unknown keys in the JSON are ignored
* on read (registry is the source of truth); keys missing from the JSON are
* treated as false. Master off short-circuits every read — isEnabled never
* returns true when master is off, so checkers bail without logs or metrics.
*
* Setters mutate CONFIG in memory and return the new value so the caller can
* persist it via configPersistence.applyConfigUpdates. .env writes happen
* there so schema validation and partial-success semantics stay consistent.
*/
'use strict';
const { CONFIG } = require('../config');
const { REGISTRY } = require('./notificationRegistry');
function parseState() {
const raw = CONFIG.NOTIFICATION_ENABLED_JSON;
if (raw === undefined || raw === null || raw === '') return {};
if (typeof raw === 'object' && !Array.isArray(raw)) return raw;
try {
const parsed = JSON.parse(String(raw));
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed;
} catch (_) {}
return {};
}
function isMasterOn() {
const v = CONFIG.NOTIFICATIONS_MASTER_ENABLED;
return v === true || v === 'true';
}
function isEnabled(alertKey) {
if (!isMasterOn()) return false;
const state = parseState();
return state[alertKey] === true;
}
function isCategoryEnabled(category) {
if (!isMasterOn()) return false;
const entries = REGISTRY[category];
if (!Array.isArray(entries) || entries.length === 0) return false;
const state = parseState();
return entries.every(e => state[e.key] === true);
}
function getAllState() {
const state = parseState();
const perKey = {};
for (const entries of Object.values(REGISTRY)) {
if (!Array.isArray(entries)) continue;
for (const e of entries) {
perKey[e.key] = state[e.key] === true;
}
}
return { master: isMasterOn(), perKey };
}
function serialize(state) {
const ordered = {};
Object.keys(state).sort().forEach(k => { ordered[k] = state[k] === true; });
return JSON.stringify(ordered);
}
function setKeyEnabled(key, enabled) {
const state = parseState();
state[String(key)] = enabled === true;
const json = serialize(state);
CONFIG.NOTIFICATION_ENABLED_JSON = json;
return json;
}
function setCategoryEnabled(category, enabled) {
const state = parseState();
const entries = REGISTRY[category];
if (Array.isArray(entries)) {
for (const e of entries) state[e.key] = enabled === true;
}
const json = serialize(state);
CONFIG.NOTIFICATION_ENABLED_JSON = json;
return json;
}
function setMasterEnabled(enabled) {
const value = enabled === true;
CONFIG.NOTIFICATIONS_MASTER_ENABLED = value;
return value;
}
module.exports = {
isEnabled,
isCategoryEnabled,
getAllState,
setKeyEnabled,
setCategoryEnabled,
setMasterEnabled
};