Pārlūkot izejas kodu

Add debug routes and WebSocket logging for real-time monitoring, support to hexagon

Erwin Jacimino 6 mēneši atpakaļ
vecāks
revīzija
8643dec37e
3 mainītis faili ar 94 papildinājumiem un 7 dzēšanām
  1. 6 0
      app.py
  2. 76 0
      routes/debug.py
  3. 12 7
      services/print_service.py

+ 6 - 0
app.py

@@ -6,6 +6,7 @@ from config.settings import DEVELOPMENT, SECRET_KEY
 from middleware.in_time import InTimeMiddleware
 from routes import store
 from routes import sales
+from routes import debug
 from services.email_service import initialize_email_sender
 from middleware.no_cache import NoCacheMiddleware
 from logging import getLogger
@@ -129,6 +130,11 @@ def setup_routes(app: FastAPI):
             tags=["Sales"], 
             dependencies=[Depends(get_current_user)]
         )
+        # Debug routes
+        logger.info("Setting up debug routes")
+        app.include_router(debug.debug_router, prefix="/api/debug", tags=["Debug"])
+        
+        #debug router
 
         logger.info("Seeting store management routes")
         app.include_router(store.store_router, prefix="/api/store", tags=["Store Management"])

+ 76 - 0
routes/debug.py

@@ -0,0 +1,76 @@
+import asyncio
+from fastapi import WebSocket, WebSocketDisconnect, Query, status
+from fastapi.routing import APIRouter
+from services.data_service import UserDataService
+from config.settings import SECRET_KEY
+from jose import jwt, JWTError
+
+# Importar tus configuraciones y servicios existentes
+# from security import SECRET_KEY, ALGORITHM, user_data_service (Ajusta según tu estructura)
+
+debug_router = APIRouter()
+user_data_service = UserDataService()
+
+async def validate_ws_token(token: str):
+    """Valida el token manualmente para WebSocket ya que no usan HTTPBearer standard"""
+    try:
+        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
+        email = payload.get("sub")
+        if email is None:
+            return None
+        
+        # Busca el usuario usando tu servicio existente
+        user = user_data_service.get_by_email(email)
+        return user
+    except (JWTError, Exception):
+        return None
+
+@debug_router.websocket("/logs")
+async def websocket_endpoint(websocket: WebSocket, token: str = Query(...)):
+    # 1. Autenticación
+    user = await validate_ws_token(token)
+    
+    if not user:
+        # Cierre con código de política violada si el token es inválido
+        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
+        return
+
+    # 2. Verificación de Permisos (Permission >= 2)
+    # Asumo que tu modelo User tiene el atributo 'permission'
+    if getattr(user, 'permission', 0) < 2:
+        await websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason="Permisos insuficientes")
+        return
+
+    await websocket.accept()
+
+    # 3. Proceso de Streaming de Logs
+    try:
+        # Ejecuta journalctl en modo 'follow' (-f) para el servicio específico
+        # -n 50 trae las ultimas 50 lineas para contexto inicial
+        process = await asyncio.create_subprocess_exec(
+            "journalctl", "-u", "pedidos_express", "-f", "-n", "50", "--output", "cat",
+            stdout=asyncio.subprocess.PIPE,
+            stderr=asyncio.subprocess.PIPE
+        )
+
+        # Bucle de lectura asíncrona
+        while True:
+            line = await process.stdout.readline()
+            if line:
+                # Decodificar bytes a string y enviar
+                await websocket.send_text(line.decode("utf-8").strip())
+            else:
+                # Pequeña pausa si no hay datos para no saturar el CPU
+                await asyncio.sleep(0.1)
+
+    except WebSocketDisconnect:
+        # El cliente se desconectó, matar el proceso de journalctl para no dejar zombies
+        if process.returncode is None:
+            process.terminate()
+            await process.wait()
+            
+    except Exception as e:
+        logger.error(f"Error en websocket logs: {e}")
+        if process.returncode is None:
+            process.terminate()
+        await websocket.close(code=status.WS_1011_INTERNAL_ERROR)

+ 12 - 7
services/print_service.py

@@ -1,3 +1,4 @@
+from typing import Optional
 import requests
 from logging import getLogger
 from config.settings import DEVELOPMENT
@@ -8,12 +9,16 @@ from enums.locations import Locations
 
 logger = getLogger(__name__)
 user_data_service = DataServiceFactory.get_user_service()
-
-def get_printer_url(location: Locations) -> str:
+is_habilited_hexagon = False
+beers_habilited = []
+def get_printer_url(location: Locations, table_number: int , products: list[str] = []) -> str:
     if DEVELOPMENT:
-        return "http://localhost:5005"
+        return "http://10.10.12.15:5004"
     if location == Locations.BAR:
-        return "http://10.10.12.11:5004"
+        if is_habilited_hexagon and table_number >= 500 and set(products).issubset(set(beers_habilited)):
+            return "http://10.10.12.15:5004"
+        else:
+            return "http://10.10.12.11:5004"
     elif location == Locations.COCTELERY:
         return "http://10.10.12.12:5004"
     elif location == Locations.PIZZAS:
@@ -24,9 +29,9 @@ def get_printer_url(location: Locations) -> str:
 def print_order(order: Order, location:Locations ):
     """Send order to printer"""
     logger.info(f"Attempting to print order for table {order.table}")
-    
+    products = list(map(lambda x: x.name, order.items))
 
-    printer_url = get_printer_url(location)
+    printer_url = get_printer_url(location, order.table, products)
 
     try:
         if not order.items or not order.table:
@@ -114,7 +119,7 @@ def get_status(location: Locations):
     """Get the status of the printer service"""
     logger.info("Checking printer service status")
     
-    printer_url = get_printer_url(location)
+    printer_url = get_printer_url(location, 1)
 
     try:
         response = requests.get(