// src/utils/pendingVoiceFixes.js // // In-memory store for pending /voicediag remediation cards. Identical // shape to pendingIgmpFixes / pendingHostAssigns — every entry is // stamped with a `timestamp` on insert, and a background sweep expires // un-acted cards after TTL_MS so the map never leaks. // // Entries hold: // { // storeNum, // personId, // Webex person id the remediation targets // personLabel, // displayName or email for message/audit // remediationId, // e.g. 'disable_dnd', 'clear_forwarding_always' // remediationPayload, // free-form; whatever the check's remediation // // handler expects (usually the pre-computed // // PUT body plus contextual identifiers). // requester, // from extractRequester(trigger) // timestamp, // auto-set on .set() // } // // TTL matches pendingIgmpFixes at 15 minutes — long enough for a // normal "let me check with the store first" handoff without leaving // a stale card interactive past the point where the underlying state // may have already been changed by hand. import { logger } from './logger.js'; const TTL_MS = 15 * 60 * 1000; const SWEEP_INTERVAL_MS = 60 * 1000; const _store = new Map(); export const pendingVoiceFixes = { set(cardId, data) { _store.set(cardId, { ...data, timestamp: Date.now() }); return this; }, get(cardId) { return _store.get(cardId); }, has(cardId) { return _store.has(cardId); }, delete(cardId) { return _store.delete(cardId); }, get size() { return _store.size; }, entries() { return _store.entries(); }, }; const sweepHandle = setInterval(() => { const now = Date.now(); for (const [cardId, data] of _store.entries()) { const stamped = typeof data?.timestamp === 'number' ? data.timestamp : 0; if (now - stamped > TTL_MS) { logger( 'voicediag:cleanup', `Expired card ${cardId} for store ${data?.storeNum || 'unknown'} ` + `remediation ${data?.remediationId || 'unknown'}`, ); _store.delete(cardId); } } }, SWEEP_INTERVAL_MS); sweepHandle.unref?.();