diff --git a/backend/routes/account.py b/backend/routes/account.py index 72985e0..e7b101a 100644 --- a/backend/routes/account.py +++ b/backend/routes/account.py @@ -80,7 +80,7 @@ def login(request: Request, login_request: LoginRequest, db: Session = Depends(g username=username, ip=client_ip, user_agent=raw_ua or "Unknown", - action="login", + action_type="login", ) raise HTTPException( status_code=403, @@ -196,7 +196,7 @@ def register(request: Request, register_request: RegisterRequest, db: Session = username=username, ip=client_ip, user_agent=raw_ua or "Unknown", - action="registration", + action_type="registration", ) raise HTTPException( status_code=403, diff --git a/backend/routes/messaging.py b/backend/routes/messaging.py index 4658200..0737d0d 100644 --- a/backend/routes/messaging.py +++ b/backend/routes/messaging.py @@ -251,31 +251,17 @@ def convert_dm_envelope(envelope: DMEnvelope) -> dict: ] } -@router.post("/send_message") -@rate_limit_per_user("30/minute") -async def send_message( - request: Request, - message_request: SendMessageRequest | None = None, - current_user: User = Depends(get_current_user), - db: Session = Depends(get_db), - # Optional multipart form support - payload: str | None = Form(default=None), - files: list[UploadFile] = File(default=[]), -): - # If payload is provided, prefer it for multipart requests - if payload and message_request is None: - # Expect JSON: {"type":"text","data":{"content": str}, "reply_to_id": number|null} - try: - obj = json.loads(payload) - content = obj.get("content", "") - reply_to_id = obj.get("reply_to_id", None) - message_request = SendMessageRequest(content=content, reply_to_id=reply_to_id) - except Exception: - raise HTTPException(status_code=400, detail="Invalid payload JSON") - - if not message_request: - raise HTTPException(status_code=400, detail="Missing request data") +async def _send_message_internal( + message_request: SendMessageRequest, + current_user: User, + db: Session, + files: list[UploadFile] = [], +) -> dict: + """Internal function to send a message without requiring a Request object. + + This can be called from both HTTP endpoints and WebSocket handlers. + """ if message_request.reply_to_id: # Check if the message being replied to exists original_message = db.query(Message).filter(Message.id == message_request.reply_to_id).first() @@ -399,6 +385,34 @@ async def send_message( return {"status": "success", "message": message_payload} +@router.post("/send_message") +@rate_limit_per_user("30/minute") +async def send_message( + request: Request, + message_request: SendMessageRequest | None = None, + current_user: User = Depends(get_current_user), + db: Session = Depends(get_db), + # Optional multipart form support + payload: str | None = Form(default=None), + files: list[UploadFile] = File(default=[]), +): + # If payload is provided, prefer it for multipart requests + if payload and message_request is None: + # Expect JSON: {"type":"text","data":{"content": str}, "reply_to_id": number|null} + try: + obj = json.loads(payload) + content = obj.get("content", "") + reply_to_id = obj.get("reply_to_id", None) + message_request = SendMessageRequest(content=content, reply_to_id=reply_to_id) + except Exception: + raise HTTPException(status_code=400, detail="Invalid payload JSON") + + if not message_request: + raise HTTPException(status_code=400, detail="Missing request data") + + return await _send_message_internal(message_request, current_user, db, files) + + @router.get("/get_messages") async def get_messages(db: Session = Depends(get_db)): messages = db.query(Message).order_by(Message.timestamp.asc()).all() @@ -963,9 +977,10 @@ class MessaggingSocketManager: raise HTTPException(401) self.user_by_ws[websocket] = current_user.id - request: SendMessageRequest = SendMessageRequest.model_validate(data["data"]) + message_request: SendMessageRequest = SendMessageRequest.model_validate(data["data"]) - response = await send_message(request, current_user, db, None, []) + # Call internal function directly (rate limiting is handled at infrastructure level via Caddy) + response = await _send_message_internal(message_request, current_user, db, []) await self.broadcast({ "type": "newMessage", "data": response["message"] diff --git a/backend/security/audit.py b/backend/security/audit.py index f848f23..cf30fdc 100644 --- a/backend/security/audit.py +++ b/backend/security/audit.py @@ -190,7 +190,7 @@ def _render_security(action: str, fields: Dict[str, Any]) -> List[str]: lines.append(f"Total entries: {total}") return lines if action == "blocked_user_agent": - action_type = fields.get("action", "access") + action_type = fields.get("action_type", "access") lines = [f"Blocked user agent attempted {action_type}"] if fields.get("username"): lines.append(f"Username: {fields['username']}")