···
// Clean up expired sessions every hour
+
const sessionCleanupInterval = setInterval(
+
cleanupExpiredSessions,
// Helper function to sync user subscriptions from Polar
async function syncUserSubscriptionsFromPolar(
···
// Periodic sync every 5 minutes as backup (SSE handles real-time updates)
+
const syncInterval = setInterval(
await whisperService.syncWithWhisper();
···
// Clean up stale files daily
+
const fileCleanupInterval = setInterval(
+
() => whisperService.cleanupStaleFiles(),
const server = Bun.serve({
port: process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000,
···
// Event-driven SSE stream with reconnection support
const stream = new ReadableStream({
async start(controller) {
+
// Track this stream for graceful shutdown
+
activeSSEStreams.add(controller);
const encoder = new TextEncoder();
let lastEventId = Math.floor(Date.now() / 1000);
···
current?.status === "failed"
+
activeSSEStreams.delete(controller);
···
clearInterval(heartbeatInterval);
transcriptionEvents.off(transcriptionId, updateHandler);
+
activeSSEStreams.delete(controller);
···
clearInterval(heartbeatInterval);
transcriptionEvents.off(transcriptionId, updateHandler);
+
activeSSEStreams.delete(controller);
···
console.log(`🪻 Thistle running at http://localhost:${server.port}`);
+
// Track active SSE streams for graceful shutdown
+
const activeSSEStreams = new Set<ReadableStreamDefaultController>();
+
// Graceful shutdown handler
+
let isShuttingDown = false;
+
async function shutdown(signal: string) {
+
if (isShuttingDown) return;
+
console.log(`\n${signal} received, starting graceful shutdown...`);
+
// 1. Stop accepting new requests
+
console.log("[Shutdown] Closing server...");
+
// 2. Close all active SSE streams (safe to kill - sync will handle reconnection)
+
console.log(`[Shutdown] Closing ${activeSSEStreams.size} active SSE streams...`);
+
for (const controller of activeSSEStreams) {
+
activeSSEStreams.clear();
+
// 3. Stop transcription service (closes streams to Murmur)
+
// 4. Stop cleanup intervals
+
console.log("[Shutdown] Stopping cleanup intervals...");
+
clearInterval(sessionCleanupInterval);
+
clearInterval(syncInterval);
+
clearInterval(fileCleanupInterval);
+
// 5. Close database connections
+
console.log("[Shutdown] Closing database...");
+
console.log("[Shutdown] Complete");
+
// Register shutdown handlers
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
+
process.on("SIGINT", () => shutdown("SIGINT"));