fix: fix missing timeouts

This commit is contained in:
Steve Korshakov 2025-09-01 11:57:27 -07:00
parent 15762fc706
commit 2dada58228
3 changed files with 43 additions and 19 deletions

View File

@ -51,7 +51,7 @@ declare module 'fastify' {
} }
export async function startApi(): Promise<{ app: FastifyInstance; io: Server }> { export async function startApi(eventRouter: EventRouter): Promise<{ app: FastifyInstance; io: Server }> {
// Configure // Configure
log('Starting API...'); log('Starting API...');
@ -249,9 +249,6 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
} }
}); });
// Initialize event router
const eventRouter = new EventRouter();
// Auth schema // Auth schema
typed.post('/v1/auth', { typed.post('/v1/auth', {
schema: { schema: {

View File

@ -1,10 +1,10 @@
import { pubsub } from "@/services/pubsub";
import { db } from "@/storage/db"; import { db } from "@/storage/db";
import { delay } from "@/utils/delay"; import { delay } from "@/utils/delay";
import { forever } from "@/utils/forever"; import { forever } from "@/utils/forever";
import { shutdownSignal } from "@/utils/shutdown"; import { shutdownSignal } from "@/utils/shutdown";
import { buildMachineActivityEphemeral, buildSessionActivityEphemeral, EventRouter } from "@/modules/eventRouter";
export function startTimeout() { export function startTimeout(eventRouter: EventRouter) {
forever('session-timeout', async () => { forever('session-timeout', async () => {
while (true) { while (true) {
// Find timed out sessions // Find timed out sessions
@ -17,16 +17,41 @@ export function startTimeout() {
} }
}); });
for (const session of sessions) { for (const session of sessions) {
await db.session.update({ const updated = await db.session.updateManyAndReturn({
where: { id: session.id }, where: { id: session.id, active: true },
data: { active: false } data: { active: false }
}); });
pubsub.emit('update-ephemeral', session.accountId, { if (updated.length === 0) {
type: 'activity', continue;
id: session.id, }
active: false, eventRouter.emitEphemeral({
activeAt: session.lastActiveAt.getTime(), userId: session.accountId,
thinking: false payload: buildSessionActivityEphemeral(session.id, false, updated[0].lastActiveAt.getTime(), false),
recipientFilter: { type: 'all-user-authenticated-connections' }
});
}
// Find timed out machines
const machines = await db.machine.findMany({
where: {
active: true,
lastActiveAt: {
lte: new Date(Date.now() - 1000 * 60 * 10) // 10 minutes
}
}
});
for (const machine of machines) {
const updated = await db.machine.updateManyAndReturn({
where: { id: machine.id, active: true },
data: { active: false }
});
if (updated.length === 0) {
continue;
}
eventRouter.emitEphemeral({
userId: machine.accountId,
payload: buildMachineActivityEphemeral(machine.id, false, updated[0].lastActiveAt.getTime()),
recipientFilter: { type: 'all-user-authenticated-connections' }
}); });
} }

View File

@ -11,6 +11,7 @@ import { startDatabaseMetricsUpdater } from "@/modules/metrics";
import { initEncrypt } from "./modules/encrypt"; import { initEncrypt } from "./modules/encrypt";
import { initGithub } from "./modules/github"; import { initGithub } from "./modules/github";
import { loadFiles } from "./storage/files"; import { loadFiles } from "./storage/files";
import { EventRouter } from "./modules/eventRouter";
async function main() { async function main() {
@ -25,6 +26,7 @@ async function main() {
await redis.ping(); await redis.ping();
// Initialize auth module // Initialize auth module
const eventRouter = new EventRouter();
await initEncrypt(); await initEncrypt();
await initGithub(); await initGithub();
await loadFiles(); await loadFiles();
@ -34,10 +36,10 @@ async function main() {
// Start // Start
// //
await startApi(); await startApi(eventRouter);
await startMetricsServer(); await startMetricsServer();
startDatabaseMetricsUpdater(); startDatabaseMetricsUpdater();
startTimeout(); startTimeout(eventRouter);
// //
// Ready // Ready
@ -56,7 +58,7 @@ process.on('uncaughtException', (error) => {
stack: error.stack, stack: error.stack,
name: error.name name: error.name
}, `Uncaught Exception: ${error.message}`); }, `Uncaught Exception: ${error.message}`);
console.error('Uncaught Exception:', error); console.error('Uncaught Exception:', error);
process.exit(1); process.exit(1);
}); });
@ -64,14 +66,14 @@ process.on('uncaughtException', (error) => {
process.on('unhandledRejection', (reason, promise) => { process.on('unhandledRejection', (reason, promise) => {
const errorMsg = reason instanceof Error ? reason.message : String(reason); const errorMsg = reason instanceof Error ? reason.message : String(reason);
const errorStack = reason instanceof Error ? reason.stack : undefined; const errorStack = reason instanceof Error ? reason.stack : undefined;
log({ log({
module: 'process-error', module: 'process-error',
level: 'error', level: 'error',
stack: errorStack, stack: errorStack,
reason: String(reason) reason: String(reason)
}, `Unhandled Rejection: ${errorMsg}`); }, `Unhandled Rejection: ${errorMsg}`);
console.error('Unhandled Rejection at:', promise, 'reason:', reason); console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1); process.exit(1);
}); });