@@ -60,6 +60,7 @@ async def root():
6060
6161
6262@app .get ("/health" )
63+ @app .head ("/health" )
6364async def health ():
6465 return {"status" : "healthy" , "timestamp" : datetime .now (UTC ).isoformat ()}
6566
@@ -173,6 +174,7 @@ async def websocket_endpoint(
173174 user_agent = websocket .headers .get ("user-agent" , "Unknown" )
174175
175176 # Get or create meeting in database
177+ meeting = None
176178 async with AsyncSessionLocal () as db :
177179 try :
178180 # Find meeting by code
@@ -185,6 +187,16 @@ async def websocket_endpoint(
185187 await websocket .close (code = 1008 , reason = "Meeting not found" )
186188 return
187189
190+ # Check if this is the first active participant (make them host)
191+ # Use a transaction-safe approach to avoid race conditions
192+ existing_participants = await db .execute (
193+ select (func .count (Participant .id ))
194+ .where (Participant .meeting_id == meeting .id )
195+ .where (Participant .is_active == True )
196+ )
197+ count = existing_participants .scalar () or 0
198+ is_host = count == 0
199+
188200 # Create participant record
189201 participant = Participant (
190202 meeting_id = meeting .id ,
@@ -193,19 +205,9 @@ async def websocket_endpoint(
193205 ip_address = client_ip ,
194206 user_agent = user_agent ,
195207 is_active = True ,
196- is_host = False , # First participant becomes host
208+ is_host = is_host ,
197209 )
198210
199- # Check if this is the first participant (make them host)
200- existing_participants = await db .execute (
201- select (func .count (Participant .id ))
202- .where (Participant .meeting_id == meeting .id )
203- .where (Participant .is_active == True )
204- )
205- count = existing_participants .scalar () or 0
206- if count == 0 :
207- participant .is_host = True
208-
209211 db .add (participant )
210212
211213 # Log join event
@@ -221,10 +223,16 @@ async def websocket_endpoint(
221223 await db .commit ()
222224 await db .refresh (participant )
223225
224- logger .info (f"Participant { client_id } created in database for meeting { meeting_code } " )
226+ logger .info (f"Participant { client_id } created in database for meeting { meeting_code } (host: { is_host } ) " )
225227 except Exception as e :
226228 logger .error (f"Error creating participant: { e } " )
227229 await db .rollback ()
230+ await websocket .close (code = 1011 , reason = "Internal server error" )
231+ return
232+
233+ # Ensure meeting was found before proceeding
234+ if not meeting :
235+ return
228236
229237 # Initialize meeting connections if not exists
230238 if meeting_code not in active_connections :
@@ -459,14 +467,20 @@ async def broadcast_to_meeting(meeting_code: str, message: dict, exclude_client:
459467 if meeting_code not in active_connections :
460468 return
461469
462- for client_id , ws in active_connections [meeting_code ].items ():
470+ # Create a list of connections to avoid modification during iteration
471+ connections = list (active_connections [meeting_code ].items ())
472+
473+ for client_id , ws in connections :
463474 if exclude_client and client_id == exclude_client :
464475 continue
465476
466477 try :
467478 await ws .send_json (message )
468479 except Exception as e :
469480 logger .error (f"Error broadcasting to { client_id } : { e } " )
481+ # Remove dead connection
482+ if meeting_code in active_connections :
483+ active_connections [meeting_code ].pop (client_id , None )
470484
471485
472486async def handle_disconnect (meeting_code : str , client_id : str , client_ip : str = None ):
0 commit comments