restart
This commit is contained in:
@@ -8,6 +8,7 @@ import { loggerMiddleware } from './middleware/logger.js';
|
||||
import env from '../config/env.js';
|
||||
import { authMiddleware } from './middleware/auth.js';
|
||||
import { ensureConnection } from '../db/store.js';
|
||||
import { closeAll as closeAllSse } from './sse.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -133,8 +134,12 @@ export async function startApi() {
|
||||
|
||||
export async function stopApi() {
|
||||
if (!server) return;
|
||||
// Long-lived SSE (and keep-alive) sockets otherwise block server.close()
|
||||
// forever — node --watch hangs on "Waiting for graceful termination...".
|
||||
closeAllSse();
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((err) => (err ? reject(err) : resolve()));
|
||||
server.closeAllConnections();
|
||||
});
|
||||
server = undefined;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* after a successful mutation so other tabs/users can refetch.
|
||||
*/
|
||||
|
||||
/** @typedef {{ res: import('express').Response, companyId: number, userId: number }} SseClient */
|
||||
/** @typedef {{ res: import('express').Response, companyId: number, userId: number, keepAlive: NodeJS.Timeout }} SseClient */
|
||||
|
||||
/** @type {Set<SseClient>} */
|
||||
const clients = new Set();
|
||||
@@ -18,16 +18,19 @@ const KEEP_ALIVE_MS = 25_000;
|
||||
*/
|
||||
export function subscribe(res, { companyId, userId }) {
|
||||
/** @type {SseClient} */
|
||||
const client = { res, companyId, userId };
|
||||
const client = {
|
||||
res,
|
||||
companyId,
|
||||
userId,
|
||||
keepAlive: setInterval(() => {
|
||||
if (res.writableEnded) return;
|
||||
res.write(': keepalive\n\n');
|
||||
}, KEEP_ALIVE_MS),
|
||||
};
|
||||
clients.add(client);
|
||||
|
||||
const keepAlive = setInterval(() => {
|
||||
if (res.writableEnded) return;
|
||||
res.write(': keepalive\n\n');
|
||||
}, KEEP_ALIVE_MS);
|
||||
|
||||
const unsubscribe = () => {
|
||||
clearInterval(keepAlive);
|
||||
clearInterval(client.keepAlive);
|
||||
clients.delete(client);
|
||||
};
|
||||
|
||||
@@ -35,6 +38,20 @@ export function subscribe(res, { companyId, userId }) {
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
/**
|
||||
* End every open SSE stream so `server.close()` can finish (e.g. node --watch).
|
||||
* EventSource clients reconnect to the new process.
|
||||
*/
|
||||
export function closeAll() {
|
||||
for (const client of [...clients]) {
|
||||
clearInterval(client.keepAlive);
|
||||
clients.delete(client);
|
||||
if (!client.res.writableEnded) {
|
||||
client.res.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a named signal to subscribers of the given company.
|
||||
* @param {string} signal - SSE event name (e.g. `category`)
|
||||
|
||||
Reference in New Issue
Block a user