Spaces:
Running
Running
Commit ·
859e00d
1
Parent(s): 8565759
checking changes
Browse files- routes/data_routes.py +5 -146
routes/data_routes.py
CHANGED
|
@@ -4,12 +4,15 @@ Data Routes for AI Engine
|
|
| 4 |
These routes handle data operations (contributor dashboard, messaging, auth)
|
| 5 |
that require MongoDB access. They are added here since the ai-engine is
|
| 6 |
what's deployed on Hugging Face Spaces.
|
|
|
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
import logging
|
| 10 |
from datetime import datetime, timezone
|
| 11 |
from typing import List, Optional, Dict, Any
|
| 12 |
-
from fastapi import APIRouter, HTTPException, Depends, Header
|
| 13 |
from pydantic import BaseModel
|
| 14 |
import jwt
|
| 15 |
import os
|
|
@@ -17,7 +20,6 @@ import os
|
|
| 17 |
from config.database import db
|
| 18 |
from config.turso import turso_db
|
| 19 |
from config.settings import settings
|
| 20 |
-
from services.websocket_manager import ws_manager
|
| 21 |
|
| 22 |
logger = logging.getLogger(__name__)
|
| 23 |
|
|
@@ -353,21 +355,13 @@ async def send_message(request: SendMessageRequest, user: dict = Depends(get_cur
|
|
| 353 |
"""Send a message to another user."""
|
| 354 |
import uuid
|
| 355 |
|
| 356 |
-
# Get sender info for real-time notification
|
| 357 |
-
sender_info = {
|
| 358 |
-
"id": user.get("id"),
|
| 359 |
-
"username": user.get("username"),
|
| 360 |
-
"avatarUrl": user.get("avatarUrl")
|
| 361 |
-
}
|
| 362 |
-
|
| 363 |
message = {
|
| 364 |
"id": str(uuid.uuid4()),
|
| 365 |
"sender_id": user["id"],
|
| 366 |
"receiver_id": request.receiver_id,
|
| 367 |
"content": request.content,
|
| 368 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 369 |
-
"read": False
|
| 370 |
-
"edited_at": None
|
| 371 |
}
|
| 372 |
|
| 373 |
# Insert into MongoDB
|
|
@@ -380,17 +374,6 @@ async def send_message(request: SendMessageRequest, user: dict = Depends(get_cur
|
|
| 380 |
except Exception as e:
|
| 381 |
logger.warning(f"Failed to insert message into Turso: {e}")
|
| 382 |
|
| 383 |
-
# Send real-time notification via WebSocket
|
| 384 |
-
try:
|
| 385 |
-
message_with_sender = {**message, "sender": sender_info}
|
| 386 |
-
await ws_manager.send_message_notification(
|
| 387 |
-
sender_id=user["id"],
|
| 388 |
-
receiver_id=request.receiver_id,
|
| 389 |
-
message_data=message_with_sender
|
| 390 |
-
)
|
| 391 |
-
except Exception as e:
|
| 392 |
-
logger.warning(f"Failed to send WebSocket notification: {e}")
|
| 393 |
-
|
| 394 |
return message
|
| 395 |
|
| 396 |
|
|
@@ -417,133 +400,9 @@ async def mark_messages_read(other_user_id: str, user: dict = Depends(get_curren
|
|
| 417 |
{"$set": {"read": True}}
|
| 418 |
)
|
| 419 |
|
| 420 |
-
# Send read receipt via WebSocket
|
| 421 |
-
try:
|
| 422 |
-
if other_user:
|
| 423 |
-
await ws_manager.send_read_receipt(
|
| 424 |
-
reader_id=user.get("id"),
|
| 425 |
-
sender_id=other_user.get("id")
|
| 426 |
-
)
|
| 427 |
-
except Exception as e:
|
| 428 |
-
logger.warning(f"Failed to send read receipt: {e}")
|
| 429 |
-
|
| 430 |
return {"marked_read": result.modified_count}
|
| 431 |
|
| 432 |
|
| 433 |
-
@router.post("/api/messaging/typing/{receiver_id}")
|
| 434 |
-
async def send_typing_indicator(
|
| 435 |
-
receiver_id: str,
|
| 436 |
-
is_typing: bool = True,
|
| 437 |
-
user: dict = Depends(get_current_user)
|
| 438 |
-
):
|
| 439 |
-
"""Send typing indicator to another user."""
|
| 440 |
-
try:
|
| 441 |
-
await ws_manager.send_typing_indicator(
|
| 442 |
-
sender_id=user["id"],
|
| 443 |
-
receiver_id=receiver_id,
|
| 444 |
-
is_typing=is_typing
|
| 445 |
-
)
|
| 446 |
-
return {"success": True}
|
| 447 |
-
except Exception as e:
|
| 448 |
-
logger.warning(f"Failed to send typing indicator: {e}")
|
| 449 |
-
return {"success": False}
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
@router.get("/api/messaging/online-users")
|
| 453 |
-
async def get_online_users(user: dict = Depends(get_current_user)):
|
| 454 |
-
"""Get list of online users."""
|
| 455 |
-
online_users = ws_manager.get_online_users()
|
| 456 |
-
return {"online_users": online_users}
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
# =============================================================================
|
| 460 |
-
# WebSocket Endpoints for Real-Time Messaging
|
| 461 |
-
# =============================================================================
|
| 462 |
-
|
| 463 |
-
async def verify_ws_token(token: str) -> Optional[dict]:
|
| 464 |
-
"""Verify JWT token for WebSocket connections."""
|
| 465 |
-
try:
|
| 466 |
-
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
|
| 467 |
-
user_id = payload.get("user_id")
|
| 468 |
-
if not user_id:
|
| 469 |
-
return None
|
| 470 |
-
user = await db.users.find_one({"id": user_id}, {"_id": 0})
|
| 471 |
-
return user
|
| 472 |
-
except Exception:
|
| 473 |
-
return None
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
@router.websocket("/ws/messaging")
|
| 477 |
-
async def websocket_messaging(
|
| 478 |
-
websocket: WebSocket,
|
| 479 |
-
token: str = Query(None)
|
| 480 |
-
):
|
| 481 |
-
"""
|
| 482 |
-
WebSocket endpoint for real-time messaging.
|
| 483 |
-
|
| 484 |
-
Connect with: ws://host/ws/messaging?token=<jwt_token>
|
| 485 |
-
|
| 486 |
-
Messages received:
|
| 487 |
-
- {"type": "new_message", "data": {...}} - New message received
|
| 488 |
-
- {"type": "typing", "data": {"sender_id": "...", "is_typing": true/false}}
|
| 489 |
-
- {"type": "read_receipt", "data": {"reader_id": "...", "message_ids": [...]}}
|
| 490 |
-
- {"type": "presence", "data": {"user_id": "...", "is_online": true/false}}
|
| 491 |
-
|
| 492 |
-
Messages to send:
|
| 493 |
-
- {"type": "ping"} - Keep connection alive
|
| 494 |
-
- {"type": "typing", "receiver_id": "...", "is_typing": true/false}
|
| 495 |
-
"""
|
| 496 |
-
# Verify token
|
| 497 |
-
if not token:
|
| 498 |
-
await websocket.close(code=4001, reason="Missing token")
|
| 499 |
-
return
|
| 500 |
-
|
| 501 |
-
user = await verify_ws_token(token)
|
| 502 |
-
if not user:
|
| 503 |
-
await websocket.close(code=4001, reason="Invalid token")
|
| 504 |
-
return
|
| 505 |
-
|
| 506 |
-
user_id = user.get("id")
|
| 507 |
-
|
| 508 |
-
# Accept connection
|
| 509 |
-
await ws_manager.connect(websocket, user_id)
|
| 510 |
-
|
| 511 |
-
# Broadcast online status
|
| 512 |
-
try:
|
| 513 |
-
await ws_manager.send_online_status(user_id, True)
|
| 514 |
-
except Exception as e:
|
| 515 |
-
logger.warning(f"Failed to broadcast online status: {e}")
|
| 516 |
-
|
| 517 |
-
try:
|
| 518 |
-
while True:
|
| 519 |
-
# Wait for messages from client
|
| 520 |
-
data = await websocket.receive_json()
|
| 521 |
-
msg_type = data.get("type")
|
| 522 |
-
|
| 523 |
-
if msg_type == "ping":
|
| 524 |
-
# Respond to ping with pong
|
| 525 |
-
await websocket.send_json({"type": "pong"})
|
| 526 |
-
|
| 527 |
-
elif msg_type == "typing":
|
| 528 |
-
# Forward typing indicator
|
| 529 |
-
receiver_id = data.get("receiver_id")
|
| 530 |
-
is_typing = data.get("is_typing", True)
|
| 531 |
-
if receiver_id:
|
| 532 |
-
await ws_manager.send_typing_indicator(user_id, receiver_id, is_typing)
|
| 533 |
-
|
| 534 |
-
except WebSocketDisconnect:
|
| 535 |
-
logger.info(f"WebSocket disconnected for user {user_id}")
|
| 536 |
-
except Exception as e:
|
| 537 |
-
logger.error(f"WebSocket error for user {user_id}: {e}")
|
| 538 |
-
finally:
|
| 539 |
-
ws_manager.disconnect(websocket, user_id)
|
| 540 |
-
# Broadcast offline status
|
| 541 |
-
try:
|
| 542 |
-
await ws_manager.send_online_status(user_id, False)
|
| 543 |
-
except Exception:
|
| 544 |
-
pass
|
| 545 |
-
|
| 546 |
-
|
| 547 |
# =============================================================================
|
| 548 |
# Maintainer Routes
|
| 549 |
# =============================================================================
|
|
|
|
| 4 |
These routes handle data operations (contributor dashboard, messaging, auth)
|
| 5 |
that require MongoDB access. They are added here since the ai-engine is
|
| 6 |
what's deployed on Hugging Face Spaces.
|
| 7 |
+
|
| 8 |
+
FIXED: Removed websocket_manager import that was causing ModuleNotFoundError
|
| 9 |
+
Build timestamp: 2026-02-09 18:42:00 UTC
|
| 10 |
"""
|
| 11 |
|
| 12 |
import logging
|
| 13 |
from datetime import datetime, timezone
|
| 14 |
from typing import List, Optional, Dict, Any
|
| 15 |
+
from fastapi import APIRouter, HTTPException, Depends, Header
|
| 16 |
from pydantic import BaseModel
|
| 17 |
import jwt
|
| 18 |
import os
|
|
|
|
| 20 |
from config.database import db
|
| 21 |
from config.turso import turso_db
|
| 22 |
from config.settings import settings
|
|
|
|
| 23 |
|
| 24 |
logger = logging.getLogger(__name__)
|
| 25 |
|
|
|
|
| 355 |
"""Send a message to another user."""
|
| 356 |
import uuid
|
| 357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
message = {
|
| 359 |
"id": str(uuid.uuid4()),
|
| 360 |
"sender_id": user["id"],
|
| 361 |
"receiver_id": request.receiver_id,
|
| 362 |
"content": request.content,
|
| 363 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 364 |
+
"read": False
|
|
|
|
| 365 |
}
|
| 366 |
|
| 367 |
# Insert into MongoDB
|
|
|
|
| 374 |
except Exception as e:
|
| 375 |
logger.warning(f"Failed to insert message into Turso: {e}")
|
| 376 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
return message
|
| 378 |
|
| 379 |
|
|
|
|
| 400 |
{"$set": {"read": True}}
|
| 401 |
)
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
return {"marked_read": result.modified_count}
|
| 404 |
|
| 405 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
# =============================================================================
|
| 407 |
# Maintainer Routes
|
| 408 |
# =============================================================================
|