sseConnectionTracker.ts RawHistoryFolder
1import { createLogger } from './utils/logger.server';
2
3const log = createLogger('sse-connection-tracker');
4
5interface ConnectionInfo {
6 userId: string;
7 connectedAt: number;
8 lastActivity: number;
9}
10
11class SSEConnectionTracker {
12 private connections = new Map<string, ConnectionInfo>();
13
14 addConnection(userId: string): boolean {
15 const existing = this.connections.get(userId);
16 if (existing) {
17 log.debug('User already has active connection', { userId });
18 // Update last activity but don't trigger status update
19 existing.lastActivity = Date.now();
20 return false; // Connection already exists
21 }
22
23 this.connections.set(userId, {
24 userId,
25 connectedAt: Date.now(),
26 lastActivity: Date.now()
27 });
28
29 log.debug('New SSE connection tracked', {
30 userId,
31 totalConnections: this.connections.size
32 });
33 return true; // New connection
34 }
35
36 removeConnection(userId: string): boolean {
37 const deleted = this.connections.delete(userId);
38 if (deleted) {
39 log.debug('SSE connection removed', {
40 userId,
41 remainingConnections: this.connections.size
42 });
43 }
44 return deleted;
45 }
46
47 hasConnection(userId: string): boolean {
48 return this.connections.has(userId);
49 }
50
51 updateActivity(userId: string): void {
52 const connection = this.connections.get(userId);
53 if (connection) {
54 connection.lastActivity = Date.now();
55 }
56 }
57
58 getActiveUserIds(): string[] {
59 return Array.from(this.connections.keys());
60 }
61
62 getConnectionCount(): number {
63 return this.connections.size;
64 }
65
66 // Clean up stale connections (e.g., if server didn't properly close them)
67 cleanupStaleConnections(maxInactivityMs: number = 5 * 60 * 1000): string[] {
68 const now = Date.now();
69 const staleUserIds: string[] = [];
70
71 for (const [userId, info] of this.connections.entries()) {
72 if (now - info.lastActivity > maxInactivityMs) {
73 this.connections.delete(userId);
74 staleUserIds.push(userId);
75 }
76 }
77
78 if (staleUserIds.length > 0) {
79 log.info('Cleaned up stale connections', {
80 count: staleUserIds.length,
81 userIds: staleUserIds
82 });
83 }
84
85 return staleUserIds;
86 }
87}
88
89// Export singleton instance
90export const sseConnectionTracker = new SSEConnectionTracker();