debug.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import asyncio
  2. from fastapi import WebSocket, WebSocketDisconnect, Query, status
  3. from fastapi.routing import APIRouter
  4. from services.data_service import UserDataService
  5. from config.settings import SECRET_KEY
  6. from jose import jwt, JWTError
  7. # Importar tus configuraciones y servicios existentes
  8. # from security import SECRET_KEY, ALGORITHM, user_data_service (Ajusta según tu estructura)
  9. debug_router = APIRouter()
  10. user_data_service = UserDataService()
  11. async def validate_ws_token(token: str):
  12. """Valida el token manualmente para WebSocket ya que no usan HTTPBearer standard"""
  13. try:
  14. payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
  15. email = payload.get("sub")
  16. if email is None:
  17. return None
  18. # Busca el usuario usando tu servicio existente
  19. user = user_data_service.get_by_email(email)
  20. return user
  21. except (JWTError, Exception):
  22. return None
  23. @debug_router.websocket("/logs")
  24. async def websocket_endpoint(websocket: WebSocket, token: str = Query(...)):
  25. # 1. Autenticación
  26. user = await validate_ws_token(token)
  27. if not user:
  28. # Cierre con código de política violada si el token es inválido
  29. await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
  30. return
  31. # 2. Verificación de Permisos (Permission >= 2)
  32. # Asumo que tu modelo User tiene el atributo 'permission'
  33. if getattr(user, 'permission', 0) < 2:
  34. await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason="Permisos insuficientes")
  35. return
  36. await websocket.accept()
  37. # 3. Proceso de Streaming de Logs
  38. try:
  39. # Ejecuta journalctl en modo 'follow' (-f) para el servicio específico
  40. # -n 50 trae las ultimas 50 lineas para contexto inicial
  41. process = await asyncio.create_subprocess_exec(
  42. "journalctl", "-u", "pedidos_express", "-f", "-n", "50", "--output", "cat",
  43. stdout=asyncio.subprocess.PIPE,
  44. stderr=asyncio.subprocess.PIPE
  45. )
  46. # Bucle de lectura asíncrona
  47. while True:
  48. line = await process.stdout.readline()
  49. if line:
  50. # Decodificar bytes a string y enviar
  51. await websocket.send_text(line.decode("utf-8").strip())
  52. else:
  53. # Pequeña pausa si no hay datos para no saturar el CPU
  54. await asyncio.sleep(0.1)
  55. except WebSocketDisconnect:
  56. # El cliente se desconectó, matar el proceso de journalctl para no dejar zombies
  57. if process.returncode is None:
  58. process.terminate()
  59. await process.wait()
  60. except Exception as e:
  61. logger.error(f"Error en websocket logs: {e}")
  62. if process.returncode is None:
  63. process.terminate()
  64. await websocket.close(code=status.WS_1011_INTERNAL_ERROR)