This commit is contained in:
2026-04-20 18:05:36 +00:00
parent d73422555d
commit 33b1f276c6
26 changed files with 598 additions and 183 deletions

View File

@@ -17,7 +17,7 @@ const { handleDiscordReply } = require('./handlers/messages');
// Services & jobs
const { sendTicketClosedEmail } = require('./services/gmail');
const { checkAutoClose, checkAutoUnclaim, reconcileDeletedTicketChannels } = require('./services/tickets');
const { checkAutoClose, checkAutoUnclaim, reconcileDeletedTicketChannels, resumePendingDeletes } = require('./services/tickets');
const { notifyAllStaffUnclaimed } = require('./services/staffNotifications');
const { registerCommands } = require('./commands/register');
const bosscordRoutes = require('./routes/bosscord');
@@ -37,6 +37,12 @@ function trackInterval(handle) {
if (handle) activeIntervals.add(handle);
return handle;
}
// Track one-shot setTimeout handles so shutdown can clear them (e.g., scheduled restarts).
const activeTimeouts = new Set();
function trackTimeout(handle) {
if (handle) activeTimeouts.add(handle);
return handle;
}
/**
* Update the Gmail poll interval at runtime.
@@ -284,8 +290,15 @@ client.once('ready', async () => {
reconcileDeletedTicketChannels(client).catch(e => console.error('reconcileDeletedTicketChannels:', e));
trackInterval(setInterval(() => reconcileDeletedTicketChannels(client).catch(e => console.error('reconcileDeletedTicketChannels:', e)), 60 * 60 * 1000));
resumePendingDeletes(client).catch(e => console.error('resumePendingDeletes:', e));
console.log('✓ Reconcile deleted ticket channels: every 1 hour');
// Start in-memory Map sweeps (per-module) — keeps long-running processes bounded.
require('./services/patternStore').startSweeps(trackInterval);
require('./services/staffNotifications').startSweeps(trackInterval);
require('./services/tickets').startTicketsSweeps(trackInterval);
console.log('✓ Memory sweeps registered: every 6 hours (unref\'d)');
if (!CONFIG.STAFF_IDS.length) {
console.warn('[surgeChecker] STAFF_IDS is not set — zero-staff detection disabled.');
}
@@ -329,8 +342,8 @@ internalApp.use('/internal', internalApi);
let httpServer = null;
let internalServer = null;
if (CONFIG.INTERNAL_API_SECRET) {
internalServer = internalApp.listen(CONFIG.INTERNAL_API_PORT, '0.0.0.0', () => {
console.log(`[internalApi] listening on 0.0.0.0:${CONFIG.INTERNAL_API_PORT}`);
internalServer = internalApp.listen(CONFIG.INTERNAL_API_PORT, '127.0.0.1', () => {
console.log(`[internalApi] listening on 127.0.0.1:${CONFIG.INTERNAL_API_PORT}`);
});
} else {
console.warn('[internalApi] INTERNAL_API_SECRET not set — internal API disabled.');
@@ -349,6 +362,10 @@ async function handleShutdown(signal) {
try { clearInterval(handle); } catch (_) {}
}
activeIntervals.clear();
for (const handle of activeTimeouts) {
try { clearTimeout(handle); } catch (_) {}
}
activeTimeouts.clear();
gmailPollInterval = null;
try { if (httpServer) await new Promise(r => httpServer.close(() => r())); } catch (_) {}
try { if (internalServer) await new Promise(r => internalServer.close(() => r())); } catch (_) {}
@@ -366,6 +383,7 @@ module.exports = {
client,
setGmailPollInterval,
clearGmailPollInterval,
trackTimeout,
sendGmailReply,
sendTicketClosedEmail,
getNextTicketNumber,