@@ -251,31 +251,17 @@ def convert_dm_envelope(envelope: DMEnvelope) -> dict:
251251 ]
252252 }
253253
254- @router .post ("/send_message" )
255- @rate_limit_per_user ("30/minute" )
256- async def send_message (
257- request : Request ,
258- message_request : SendMessageRequest | None = None ,
259- current_user : User = Depends (get_current_user ),
260- db : Session = Depends (get_db ),
261- # Optional multipart form support
262- payload : str | None = Form (default = None ),
263- files : list [UploadFile ] = File (default = []),
264- ):
265- # If payload is provided, prefer it for multipart requests
266- if payload and message_request is None :
267- # Expect JSON: {"type":"text","data":{"content": str}, "reply_to_id": number|null}
268- try :
269- obj = json .loads (payload )
270- content = obj .get ("content" , "" )
271- reply_to_id = obj .get ("reply_to_id" , None )
272- message_request = SendMessageRequest (content = content , reply_to_id = reply_to_id )
273- except Exception :
274- raise HTTPException (status_code = 400 , detail = "Invalid payload JSON" )
275-
276- if not message_request :
277- raise HTTPException (status_code = 400 , detail = "Missing request data" )
278254
255+ async def _send_message_internal (
256+ message_request : SendMessageRequest ,
257+ current_user : User ,
258+ db : Session ,
259+ files : list [UploadFile ] = [],
260+ ) -> dict :
261+ """Internal function to send a message without requiring a Request object.
262+
263+ This can be called from both HTTP endpoints and WebSocket handlers.
264+ """
279265 if message_request .reply_to_id :
280266 # Check if the message being replied to exists
281267 original_message = db .query (Message ).filter (Message .id == message_request .reply_to_id ).first ()
@@ -399,6 +385,34 @@ async def send_message(
399385 return {"status" : "success" , "message" : message_payload }
400386
401387
388+ @router .post ("/send_message" )
389+ @rate_limit_per_user ("30/minute" )
390+ async def send_message (
391+ request : Request ,
392+ message_request : SendMessageRequest | None = None ,
393+ current_user : User = Depends (get_current_user ),
394+ db : Session = Depends (get_db ),
395+ # Optional multipart form support
396+ payload : str | None = Form (default = None ),
397+ files : list [UploadFile ] = File (default = []),
398+ ):
399+ # If payload is provided, prefer it for multipart requests
400+ if payload and message_request is None :
401+ # Expect JSON: {"type":"text","data":{"content": str}, "reply_to_id": number|null}
402+ try :
403+ obj = json .loads (payload )
404+ content = obj .get ("content" , "" )
405+ reply_to_id = obj .get ("reply_to_id" , None )
406+ message_request = SendMessageRequest (content = content , reply_to_id = reply_to_id )
407+ except Exception :
408+ raise HTTPException (status_code = 400 , detail = "Invalid payload JSON" )
409+
410+ if not message_request :
411+ raise HTTPException (status_code = 400 , detail = "Missing request data" )
412+
413+ return await _send_message_internal (message_request , current_user , db , files )
414+
415+
402416@router .get ("/get_messages" )
403417async def get_messages (db : Session = Depends (get_db )):
404418 messages = db .query (Message ).order_by (Message .timestamp .asc ()).all ()
@@ -963,9 +977,10 @@ def get_current_user_inner() -> User | None:
963977 raise HTTPException (401 )
964978 self .user_by_ws [websocket ] = current_user .id
965979
966- request : SendMessageRequest = SendMessageRequest .model_validate (data ["data" ])
980+ message_request : SendMessageRequest = SendMessageRequest .model_validate (data ["data" ])
967981
968- response = await send_message (request , current_user , db , None , [])
982+ # Call internal function directly (rate limiting is handled at infrastructure level via Caddy)
983+ response = await _send_message_internal (message_request , current_user , db , [])
969984 await self .broadcast ({
970985 "type" : "newMessage" ,
971986 "data" : response ["message" ]
0 commit comments