@@ -48,9 +48,11 @@ class WebSocketService {
4848 private processId : string | null = null ; // Add this to store the process ID
4949 private reconnectAttempts = 0 ;
5050 private maxReconnectAttempts = 5 ;
51- private reconnectDelay = 1000 ;
51+ private reconnectDelay = 12000 ; // Changed from 1000ms to 12000ms (12 seconds)
5252 private listeners : Map < string , Set < ( message : StreamMessage ) => void > > = new Map ( ) ;
5353 private planSubscriptions : Set < string > = new Set ( ) ;
54+ private reconnectTimer : NodeJS . Timeout | null = null ; // Add timer tracking
55+ private isConnecting = false ; // Add connection state tracking
5456
5557 /**
5658 * Connect to WebSocket server
@@ -79,51 +81,72 @@ class WebSocketService {
7981 } ) ;
8082 }
8183 return new Promise ( ( resolve , reject ) => {
84+ // Prevent multiple simultaneous connection attempts
85+ if ( this . isConnecting ) {
86+ console . log ( 'Connection attempt already in progress' ) ;
87+ return ;
88+ }
89+
90+ // Clear any existing reconnection timer
91+ if ( this . reconnectTimer ) {
92+ clearTimeout ( this . reconnectTimer ) ;
93+ this . reconnectTimer = null ;
94+ }
95+
8296 try {
97+ this . isConnecting = true ;
98+
8399 // Get WebSocket URL from environment or default to localhost
84100 const wsProtocol = window . location . protocol === 'https:' ? 'wss:' : 'ws:' ;
85101 const wsHost = process . env . REACT_APP_WS_HOST || '127.0.0.1:8000' ;
86102 const processId = crypto . randomUUID ( ) ; // Generate unique process ID for this session
87103
88- // const wsUrl = `${wsProtocol}//${wsHost}/api/v3/socket/${processId}`;
89104 // Build WebSocket URL with authentication headers as query parameters
90- const userId = getUserId ( ) ; // Import this from config
91- const wsUrl = `${ wsProtocol } //${ wsHost } /api/v3/socket/${ processId } ?user_id=${ encodeURIComponent ( userId ) } ` ;
105+ const userId = getUserId ( ) ; // Import this from config
106+ const wsUrl = `${ wsProtocol } //${ wsHost } /api/v3/socket/${ processId } ?user_id=${ encodeURIComponent ( userId ) } ` ;
92107
93108 console . log ( 'Connecting to WebSocket:' , wsUrl ) ;
94109
95110 this . ws = new WebSocket ( wsUrl ) ;
96111
97112 this . ws . onopen = ( ) => {
98- console . log ( 'WebSocket connected' ) ;
113+ console . log ( 'WebSocket connected successfully ' ) ;
99114 this . reconnectAttempts = 0 ;
115+ this . isConnecting = false ;
100116 this . emit ( 'connection_status' , { connected : true } ) ;
101117 resolve ( ) ;
102118 } ;
103119
104120 this . ws . onmessage = ( event ) => {
105121 try {
106- const message : StreamMessage = JSON . parse ( event . data ) ;
122+ const message : StreamMessage = JSON . parse ( event . data ) ;
107123 this . handleMessage ( message ) ;
108124 } catch ( error ) {
109125 console . error ( 'Error parsing WebSocket message:' , error , 'Raw data:' , event . data ) ;
110126 this . emit ( 'error' , { error : 'Failed to parse WebSocket message' } ) ;
111127 }
112128 } ;
113129
114- this . ws . onclose = ( ) => {
115- console . log ( 'WebSocket disconnected' ) ;
130+ this . ws . onclose = ( event ) => {
131+ console . log ( 'WebSocket disconnected' , event . code , event . reason ) ;
132+ this . isConnecting = false ;
116133 this . emit ( 'connection_status' , { connected : false } ) ;
117- this . attemptReconnect ( ) ;
134+
135+ // Only attempt reconnect if it wasn't a manual disconnect
136+ if ( event . code !== 1000 ) { // 1000 = normal closure
137+ this . attemptReconnect ( ) ;
138+ }
118139 } ;
119140
120141 this . ws . onerror = ( error ) => {
121142 console . error ( 'WebSocket error:' , error ) ;
143+ this . isConnecting = false ;
122144 this . emit ( 'error' , { error : 'WebSocket connection failed' } ) ;
123145 reject ( error ) ;
124146 } ;
125147
126148 } catch ( error ) {
149+ this . isConnecting = false ;
127150 reject ( error ) ;
128151 }
129152 } ) ;
@@ -133,11 +156,22 @@ class WebSocketService {
133156 * Disconnect from WebSocket server
134157 */
135158 disconnect ( ) : void {
159+ console . log ( 'Manually disconnecting WebSocket' ) ;
160+
161+ // Clear any pending reconnection attempts
162+ if ( this . reconnectTimer ) {
163+ clearTimeout ( this . reconnectTimer ) ;
164+ this . reconnectTimer = null ;
165+ }
166+
167+ this . reconnectAttempts = this . maxReconnectAttempts ; // Prevent reconnection
168+
136169 if ( this . ws ) {
137- this . ws . close ( ) ;
170+ this . ws . close ( 1000 , 'Manual disconnect' ) ; // Use normal closure code
138171 this . ws = null ;
139172 }
140173 this . planSubscriptions . clear ( ) ;
174+ this . isConnecting = false ;
141175 }
142176
143177 /**
@@ -232,9 +266,6 @@ class WebSocketService {
232266 /**
233267 * Handle incoming WebSocket messages
234268 */
235- /**
236- * Handle incoming WebSocket messages
237- */
238269 private handleMessage ( message : StreamMessage ) : void {
239270 console . log ( 'WebSocket message received:' , message ) ;
240271
@@ -257,26 +288,39 @@ class WebSocketService {
257288 */
258289 private attemptReconnect ( ) : void {
259290 if ( this . reconnectAttempts >= this . maxReconnectAttempts ) {
260- console . log ( 'Max reconnection attempts reached' ) ;
291+ console . log ( 'Max reconnection attempts reached - stopping reconnect attempts ' ) ;
261292 this . emit ( 'error' , { error : 'Max reconnection attempts reached' } ) ;
262293 return ;
263294 }
264295
296+ // Prevent multiple simultaneous reconnection attempts
297+ if ( this . isConnecting || this . reconnectTimer ) {
298+ console . log ( 'Reconnection attempt already in progress' ) ;
299+ return ;
300+ }
301+
265302 this . reconnectAttempts ++ ;
303+ // Use exponential backoff: 12s, 24s, 48s, 96s, 192s
266304 const delay = this . reconnectDelay * Math . pow ( 2 , this . reconnectAttempts - 1 ) ;
267305
268- console . log ( `Attempting to reconnect in ${ delay } ms (attempt ${ this . reconnectAttempts } ) ` ) ;
306+ console . log ( `Scheduling reconnection attempt ${ this . reconnectAttempts } / ${ this . maxReconnectAttempts } in ${ delay / 1000 } s ` ) ;
269307
270- setTimeout ( ( ) => {
308+ this . reconnectTimer = setTimeout ( ( ) => {
309+ this . reconnectTimer = null ;
310+ console . log ( `Attempting reconnection (attempt ${ this . reconnectAttempts } )` ) ;
311+
271312 this . connect ( )
272313 . then ( ( ) => {
314+ console . log ( 'Reconnection successful - re-subscribing to plans' ) ;
273315 // Re-subscribe to all plans
274316 this . planSubscriptions . forEach ( planId => {
275317 this . subscribeToPlan ( planId ) ;
276318 } ) ;
277319 } )
278320 . catch ( ( error ) => {
279321 console . error ( 'Reconnection failed:' , error ) ;
322+ // The connect() method will trigger another reconnection attempt
323+ // through the onclose handler if needed
280324 } ) ;
281325 } , delay ) ;
282326 }
@@ -300,28 +344,28 @@ class WebSocketService {
300344 }
301345
302346 /**
303- * Send plan approval response
304- */
347+ * Send plan approval response
348+ */
305349 sendPlanApprovalResponse ( response : PlanApprovalResponseData ) : void {
306- if ( ! this . ws || this . ws . readyState !== WebSocket . OPEN ) {
307- console . error ( 'WebSocket not connected - cannot send plan approval response' ) ;
308- this . emit ( 'error' , { error : 'Cannot send plan approval response - WebSocket not connected' } ) ;
309- return ;
310- }
350+ if ( ! this . ws || this . ws . readyState !== WebSocket . OPEN ) {
351+ console . error ( 'WebSocket not connected - cannot send plan approval response' ) ;
352+ this . emit ( 'error' , { error : 'Cannot send plan approval response - WebSocket not connected' } ) ;
353+ return ;
354+ }
311355
312- try {
313- const message = {
314- type : 'plan_approval_response' ,
315- data : response
316- } ;
317- this . ws . send ( JSON . stringify ( message ) ) ;
318- console . log ( 'Plan approval response sent:' , response ) ;
319- } catch ( error ) {
320- console . error ( 'Failed to send plan approval response:' , error ) ;
321- this . emit ( 'error' , { error : 'Failed to send plan approval response' } ) ;
356+ try {
357+ const message = {
358+ type : 'plan_approval_response' ,
359+ data : response
360+ } ;
361+ this . ws . send ( JSON . stringify ( message ) ) ;
362+ console . log ( 'Plan approval response sent:' , response ) ;
363+ } catch ( error ) {
364+ console . error ( 'Failed to send plan approval response:' , error ) ;
365+ this . emit ( 'error' , { error : 'Failed to send plan approval response' } ) ;
366+ }
322367 }
323368}
324- }
325369
326370// Export singleton instance
327371export const webSocketService = new WebSocketService ( ) ;
0 commit comments