latapp 9 kuukautta sitten
vanhempi
sitoutus
53f9152b4e
13 muutettua tiedostoa jossa 746 lisäystä ja 1291 poistoa
  1. 12 61
      app.py
  2. 8 101
      auth/security.py
  3. 1 0
      config/messages.py
  4. 8 72
      main.py
  5. 4 1
      models/user.py
  6. 604 0
      public/pin_forgot.html
  7. 2 57
      routes/chat.py
  8. 16 354
      routes/orders.py
  9. 44 187
      routes/users.py
  10. 11 121
      services/email_service.py
  11. 0 213
      services/logging_service.py
  12. 13 124
      services/print_service.py
  13. 23 0
      services/recovery_service.py

+ 12 - 61
app.py

@@ -1,10 +1,11 @@
+from math import log
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from starlette.middleware.sessions import SessionMiddleware
 from config.settings import DEVELOPMENT, SECRET_KEY
+from models import user
 from routes import sales
 from services.email_service import initialize_email_sender
-from services.logging_service import structured_logger, LogLevel
 from middleware.no_cache import NoCacheMiddleware
 from logging import getLogger
 
@@ -31,24 +32,12 @@ def create_app() -> FastAPI:
                 redoc_url=None
             )
 
-        structured_logger.log_system_event(
-            "FastAPI application created",
-            LogLevel.INFO,
-            {
-                "development_mode": DEVELOPMENT,
-                "docs_enabled": DEVELOPMENT,
-                "title": "Web Pedidos Klein - FastAPI Backend"
-            }
-        )
 
         # Initialize email sender
         logger.info("Initializing email sender")
         initialize_email_sender()
-        
-        structured_logger.log_email_event(
-            "Email sender initialized",
-            LogLevel.INFO
-        )
+
+        logger.info("Email sender initialized")
 
         # Add CORS middleware
         logger.info("Adding CORS middleware")
@@ -60,14 +49,7 @@ def create_app() -> FastAPI:
             allow_headers=["*"],
         )
         
-        structured_logger.log_system_event(
-            "CORS middleware configured",
-            LogLevel.INFO,
-            {
-                "allowed_origins": ["https://admin.kleinexpress.store"],
-                "allow_credentials": True
-            }
-        )
+        
         
         # Add SessionMiddleware
         logger.info("Adding session middleware")
@@ -77,21 +59,12 @@ def create_app() -> FastAPI:
             max_age=60 * 60  # max_age in seconds for Starlette
         )
         
-        structured_logger.log_system_event(
-            "Session middleware configured",
-            LogLevel.INFO,
-            {"max_age_seconds": 3600}
-        )
         
         # Add NoCacheMiddleware
         logger.info("Adding no-cache middleware")
         app.add_middleware(NoCacheMiddleware)
         
-        structured_logger.log_system_event(
-            "No-cache middleware configured",
-            LogLevel.INFO,
-            {"description": "Middleware for preventing cache on public files"}
-        )
+        
         
         logger.info("FastAPI application created successfully")
         return app
@@ -99,14 +72,7 @@ def create_app() -> FastAPI:
     except Exception as e:
         error_msg = f"Error creating FastAPI application: {e}"
         logger.error(error_msg)
-        structured_logger.log_system_event(
-            "FastAPI application creation failed",
-            LogLevel.ERROR,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+        
         raise
 
 
@@ -162,7 +128,9 @@ def setup_routes(app: FastAPI):
         # Verification routes
         logger.info("Setting up verification routes")
         app.include_router(users.verify_router, prefix="/verify", tags=["Verification"])
-        
+
+        app.include_router(users.recovery_pin_router, prefix="/recovery", tags=["Recovery PIN"])
+
         # Static routes
         logger.info("Setting up static routes")
         from fastapi.responses import HTMLResponse
@@ -178,28 +146,11 @@ def setup_routes(app: FastAPI):
         static.mount_main_static_files(app)
         static.mount_register_static_files(app)
 
-        structured_logger.log_system_event(
-            "All application routes configured successfully",
-            LogLevel.INFO,
-            {
-                "route_groups": [
-                    "chat", "users", "products", "orders", 
-                    "sales", "verification", "static"
-                ]
-            }
-        )
         
         logger.info("Application routes setup completed successfully")
         
     except Exception as e:
-        error_msg = f"Error setting up application routes: {e}"
+        error_msg = f"Error setting up application routes: - {e}"
         logger.error(error_msg)
-        structured_logger.log_system_event(
-            "Route setup failed",
-            LogLevel.ERROR,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+    
         raise

+ 8 - 101
auth/security.py

@@ -11,7 +11,6 @@ from passlib.context import CryptContext
 
 from models.user import User
 from services.data_service import UserDataService
-from services.logging_service import structured_logger, LogLevel
 
 logger = getLogger(__name__)
 
@@ -38,16 +37,6 @@ def generate_token(email: str):
         expires_delta = timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)
         token = create_access_token(data=data, expires_delta=expires_delta)
         
-        structured_logger.log_security_event(
-            f"JWT token generated for user {email}",
-            LogLevel.INFO,
-            {
-                "email": email,
-                "token_length": len(token),
-                "expires_in_days": ACCESS_TOKEN_EXPIRE_DAYS
-            },
-            user_email=email
-        )
         
         logger.debug(f"Generated token for email {email}: {token[:20]}...")
         return token
@@ -55,16 +44,7 @@ def generate_token(email: str):
     except Exception as e:
         error_msg = f"Failed to generate token for {email}: {e}"
         logger.error(error_msg)
-        structured_logger.log_security_event(
-            f"Token generation failed for user {email}",
-            LogLevel.ERROR,
-            {
-                "email": email,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_email=email
-        )
+        
         raise
 
 def create_access_token(data: dict, expires_delta: timedelta) -> str:
@@ -88,45 +68,17 @@ def authenticate_user(email: str, password: str) -> bool:
         user = user_data_service.login(email, password)
         
         if user:
-            structured_logger.log_security_event(
-                f"Successful authentication for user {email}",
-                LogLevel.INFO,
-                {
-                    "email": email,
-                    "user_id": user.id,
-                    "user_name": user.name
-                },
-                user_id=user.id,
-                user_email=email
-            )
             logger.info(f"Authentication successful for user: {email}")
             return True
         else:
-            structured_logger.log_security_event(
-                f"Failed authentication attempt for user {email}",
-                LogLevel.WARNING,
-                {
-                    "email": email,
-                    "reason": "Invalid credentials"
-                },
-                user_email=email
-            )
+            
             logger.warning(f"Authentication failed for user: {email}")
             return False
             
     except Exception as e:
         error_msg = f"Authentication error for {email}: {e}"
         logger.error(error_msg)
-        structured_logger.log_security_event(
-            f"Authentication error for user {email}",
-            LogLevel.ERROR,
-            {
-                "email": email,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_email=email
-        )
+        
         return False
 
 async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)) -> User:
@@ -145,36 +97,18 @@ async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(s
         
         if email is None:
             logger.error("Token does not contain email")
-            structured_logger.log_security_event(
-                "Token validation failed: missing email",
-                LogLevel.WARNING,
-                {"reason": "Token missing email subject"}
-            )
+            
             raise credentials_exception
             
         token_data = TokenData(email=email)
         
     except JWTError as e:
         logger.error(f"JWTError: Invalid token - {e}")
-        structured_logger.log_security_event(
-            "Token validation failed: JWT error",
-            LogLevel.WARNING,
-            {
-                "error": str(e),
-                "error_type": "JWTError"
-            }
-        )
+        
         raise credentials_exception
     except Exception as e:
         logger.error(f"Unexpected error during token validation: {e}")
-        structured_logger.log_security_event(
-            "Token validation failed: unexpected error",
-            LogLevel.ERROR,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+        
         raise credentials_exception
     
     try:
@@ -182,42 +116,15 @@ async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(s
         
         if user is None:
             logger.error(f"User not found: {token_data.email}")
-            structured_logger.log_security_event(
-                f"Token validation failed: user not found",
-                LogLevel.WARNING,
-                {
-                    "email": token_data.email,
-                    "reason": "User not found in database"
-                },
-                user_email=token_data.email
-            )
+            
             raise credentials_exception
             
         logger.debug(f"User authenticated successfully: {user.email}")
-        structured_logger.log_security_event(
-            f"Token validation successful for user {user.email}",
-            LogLevel.DEBUG,
-            {
-                "user_id": user.id,
-                "email": user.email,
-                "permissions": user.permissions
-            },
-            user_id=user.id,
-            user_email=user.email
-        )
         
         return user
         
     except Exception as e:
         logger.error(f"Database error during user lookup: {e}")
-        structured_logger.log_security_event(
-            "Token validation failed: database error",
-            LogLevel.ERROR,
-            {
-                "email": token_data.email if 'token_data' in locals() else "unknown",
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+    
         raise credentials_exception
 

+ 1 - 0
config/messages.py

@@ -28,6 +28,7 @@ class SuccessResponse:
     PRODUCT_CREATE_SUCCESS = "Producto creado exitosamente."
     PRODUCT_DELETE_SUCCESS = "Producto eliminado exitosamente."
     REWARD_SUCCESS = "Recompensa aplicada exitosamente."
+    RECOVERY_EMAIL_SENT = "Se ha enviado un correo electrónico de recuperación."
 class UserResponse:
     """Class to handle user-related messages in the response."""
 

+ 8 - 72
main.py

@@ -1,10 +1,8 @@
+from config.settings import PORT, OPENAI_API_KEY, BG_DATA_PATH,DEVELOPMENT, validate_config
 import os
 import uvicorn
 from app import create_app, setup_routes
-from config.settings import PORT, OPENAI_API_KEY, BG_DATA_PATH,DEVELOPMENT, validate_config
 from logging import getLogger
-from threading import Thread
-from services.logging_service import structured_logger, LogLevel
 
 from services.data_service import initialize_db
 
@@ -14,35 +12,19 @@ def main():
     """Main application entry point"""
     logger.info("Starting Pedidos Express application")
     
-    structured_logger.log_system_event(
-        "Application startup initiated",
-        LogLevel.INFO,
-        {
-            "port": PORT,
-            "development_mode": DEVELOPMENT,
-            "bg_data_path": BG_DATA_PATH
-        }
-    )
     
     try:
         # Validate configuration
         if not validate_config():
             logger.critical("FATAL: Configuration validation failed.")
-            structured_logger.log_system_event(
-                "Application startup failed: configuration validation error",
-                LogLevel.CRITICAL,
-                {"validation_step": "configuration"}
-            )
+            
             if not OPENAI_API_KEY:
                 logger.error("Please create a .env file with OPENAI_API_KEY='your_key_here'")
                 with open(".env", "w") as f:
                     f.write("OPENAI_API_KEY='your_key_here'")
             return None
         
-        structured_logger.log_system_event(
-            "Configuration validation successful",
-            LogLevel.INFO
-        )
+        
         
         # Create and configure app
         logger.info("Creating FastAPI application")
@@ -51,88 +33,42 @@ def main():
         logger.info("Setting up application routes")
         setup_routes(app)
         
-        structured_logger.log_system_event(
-            "FastAPI application created and routes configured",
-            LogLevel.INFO
-        )
         
         # Initialize database
         logger.info("Initializing database")
         initialize_db()
         
-        structured_logger.log_system_event(
-            "Database initialization completed",
-            LogLevel.INFO
-        )
         
         # Display startup information
         logger.info(f"Server starting on http://localhost:{PORT}")
         
         if not os.path.exists(BG_DATA_PATH):
             logger.warning(f"WARNING: {BG_DATA_PATH} not found. AI assistant will not have specific menu data.")
-            structured_logger.log_system_event(
-                "AI assistant data file not found",
-                LogLevel.WARNING,
-                {
-                    "bg_data_path": BG_DATA_PATH,
-                    "impact": "AI assistant will have limited functionality"
-                }
-            )
+        
         else:
             logger.info(f"AI assistant data loaded from: {os.path.abspath(BG_DATA_PATH)}")
-            structured_logger.log_system_event(
-                "AI assistant data loaded successfully",
-                LogLevel.INFO,
-                {"bg_data_path": os.path.abspath(BG_DATA_PATH)}
-            )
+        
+
 
-        structured_logger.log_system_event(
-            "Application startup completed successfully",
-            LogLevel.INFO,
-            {
-                "server_url": f"http://localhost:{PORT}",
-                "development_mode": DEVELOPMENT
-            }
-        )
         
         logger.info("Pedidos Express application ready to serve requests")
         return app
         
     except Exception as e:
-        error_msg = f"Critical error during application startup: {e}"
+        error_msg = f"Critical error during application startup: - {e}"
         logger.critical(error_msg)
         
-        structured_logger.log_system_event(
-            "Application startup failed with critical error",
-            LogLevel.CRITICAL,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
         return None
 
 app = main()
 
 if not app:
     logger.critical("FATAL: Failed to create FastAPI app.")
-    structured_logger.log_system_event(
-        "Application failed to start - FastAPI app creation failed",
-        LogLevel.CRITICAL
-    )
+
     exit(1)
 
 logger.info("Application initialized successfully")
 
 if __name__ == "__main__":
     logger.info(f"Starting server with uvicorn on port {PORT}")
-    structured_logger.log_system_event(
-        "Starting uvicorn server",
-        LogLevel.INFO,
-        {
-            "host": "0.0.0.0",
-            "port": PORT,
-            "log_level": "info"
-        }
-    )
     uvicorn.run(app, host="0.0.0.0", port=PORT, log_level="info", access_log=False)

+ 4 - 1
models/user.py

@@ -30,4 +30,7 @@ class User(BaseModel):
 
 class LoginRequest(BaseModel):
     email: str
-    pin: str = Field(min_length=4, max_length=4, description="4-digit PIN for user authentication")
+    pin: str = Field(min_length=4, max_length=4, description="4-digit PIN for user authentication")
+
+class PinRecoveryRequest(BaseModel):
+    email: str

+ 604 - 0
public/pin_forgot.html

@@ -0,0 +1,604 @@
+<!DOCTYPE html>
+<html lang="es">
+<head>
+  <meta charset="UTF-8" />
+  <title>Recuperar PIN - Biergarten Klein</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  
+  <!-- Meta tags para evitar cache -->
+  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
+  <meta http-equiv="Pragma" content="no-cache">
+  <meta http-equiv="Expires" content="0">
+  
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link rel="stylesheet" as="style" onload="this.rel='stylesheet'"
+        href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans:wght@400;500;700;900&family=Spline+Sans:wght@400;500;700">
+  <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
+   <script>
+        tailwind.config = {
+            theme: {
+                extend: {
+                    colors: {
+                        'custom-dark': '#101419',
+                        'custom-dark-hover': '#37404a',
+                        'gray-50': '#f9fafb',
+                        'gray-100': '#f3f4f6',
+                    }
+                }
+            }
+        }
+    </script>
+</head>
+
+<body class="min-h-screen bg-gray-50 flex items-center justify-center p-4" style='font-family:"Spline Sans","Noto Sans",sans-serif;'>
+  
+  <div class="w-full max-w-md">
+    <!-- Header -->
+    <div class="text-center mb-8">
+      <div class="inline-flex items-center justify-center w-16 h-16 bg-[#101419] rounded-full mb-4">
+        <svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
+        </svg>
+      </div>
+      <h1 class="text-[26px] font-bold text-[#101419] tracking-tight mb-2">
+        ¿Olvidaste tu PIN?
+      </h1>
+      <p class="text-[#58728d] text-sm leading-relaxed">
+        No te preocupes, te ayudamos a recuperar el acceso a tu cuenta
+      </p>
+    </div>
+
+    <!-- Indicador de pasos -->
+    <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
+      <div class="space-y-4">
+        <div class="flex items-start space-x-3" id="step1Indicator">
+          <div class="flex-shrink-0 w-6 h-6 bg-[#101419] text-white rounded-full flex items-center justify-center text-xs font-medium">1</div>
+          <div class="text-sm">
+            <p class="font-medium text-[#101419]">Ingresa tu correo</p>
+            <p class="text-[#58728d] mt-1">Te enviaremos un código de verificación</p>
+          </div>
+        </div>
+        <div class="flex items-start space-x-3" id="step2Indicator">
+          <div class="flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">2</div>
+          <div class="text-sm">
+            <p class="font-medium text-gray-400">Código de verificación</p>
+            <p class="text-gray-400 mt-1">Ingresa el código de 6 dígitos</p>
+          </div>
+        </div>
+        <div class="flex items-start space-x-3" id="step3Indicator">
+          <div class="flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">3</div>
+          <div class="text-sm">
+            <p class="font-medium text-gray-400">Nuevo PIN</p>
+            <p class="text-gray-400 mt-1">Crea tu nuevo PIN de 4 dígitos</p>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- FASE 1: Ingreso de correo -->
+    <form id="emailForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6">
+      <div class="text-center">
+        <h2 class="text-[19px] font-bold text-[#101419] mb-2">Ingresa tu correo electrónico</h2>
+        <p class="text-sm text-[#58728d]">
+          Te enviaremos un código de verificación a tu correo
+        </p>
+      </div>
+
+      <div class="space-y-4">
+        <div>
+          <label for="emailInput" class="block text-sm font-medium text-[#101419] mb-2">
+            Correo electrónico 
+          </label>
+          <input 
+            id="emailInput"
+            name="email"
+            type="email"
+            class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all"
+            placeholder="tu@email.com"
+            required
+          />
+        </div>
+      </div>
+
+      <div class="space-y-3">
+        <button 
+          id="emailSubmitBtn"
+          type="submit"
+          class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
+        >
+          Enviar código
+        </button>
+        
+        <a 
+          href="/"
+          class="block w-full text-center border border-gray-300 hover:border-[#101419] text-[#101419] py-3 rounded-lg font-medium transition-colors duration-200"
+        >
+          Volver al inicio
+        </a>
+      </div>
+    </form>
+
+    <!-- FASE 2: Verificación de código -->
+    <form id="codeForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6 hidden">
+      <div class="text-center">
+        <h2 class="text-[19px] font-bold text-[#101419] mb-2">Código de verificación</h2>
+        <p class="text-sm text-[#58728d]">
+          Ingresa el código de 6 dígitos que enviamos a <span id="emailDisplay" class="font-medium"></span>
+        </p>
+      </div>
+
+      <div class="space-y-4">
+        <div>
+          <label for="codeInput" class="block text-sm font-medium text-[#101419] mb-2">
+            Código de verificación
+          </label>
+          <input 
+            id="codeInput"
+            name="code"
+            type="text"
+            maxlength="6"
+            class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
+            placeholder="000000"
+            required
+          />
+        </div>
+
+        <div class="text-center">
+          <button 
+            type="button"
+            id="resendCodeBtn"
+            class="text-sm text-[#58728d] hover:text-[#101419] transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:text-[#58728d]"
+          >
+            ¿No recibiste el código? <span class="font-medium" id="resendText">Reenviar</span>
+          </button>
+        </div>
+      </div>
+
+      <div class="space-y-3">
+        <button 
+          id="codeSubmitBtn"
+          type="submit"
+          class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
+        >
+          Verificar código
+        </button>
+        
+        <button 
+          type="button"
+          id="backToEmailBtn"
+          class="block w-full text-center border border-gray-300 hover:border-[#101419] text-[#101419] py-3 rounded-lg font-medium transition-colors duration-200"
+        >
+          Cambiar correo
+        </button>
+      </div>
+    </form>
+
+    <!-- FASE 3: Crear nuevo PIN -->
+    <form id="pinForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6 hidden">
+      <div class="text-center">
+        <h2 class="text-[19px] font-bold text-[#101419] mb-2">Crea tu nuevo PIN</h2>
+        <p class="text-sm text-[#58728d]">
+          Ingresa un PIN de 4 dígitos que sea fácil de recordar para ti
+        </p>
+      </div>
+
+      <div class="space-y-4">
+        <div>
+          <label for="newPinInput" class="block text-sm font-medium text-[#101419] mb-2">
+            Nuevo PIN
+          </label>
+          <input 
+            id="newPinInput"
+            name="newPin"
+            type="password"
+            maxlength="4"
+            class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
+            placeholder="••••"
+            required
+          />
+        </div>
+
+        <div>
+          <label for="confirmPinInput" class="block text-sm font-medium text-[#101419] mb-2">
+            Confirmar PIN
+          </label>
+          <input 
+            id="confirmPinInput"
+            name="confirmPin"
+            type="password"
+            maxlength="4"
+            class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
+            placeholder="••••"
+            required
+          />
+        </div>
+
+        <div class="bg-blue-50 border border-blue-200 text-blue-800 px-4 py-3 rounded-lg text-sm">
+          <div class="flex items-start space-x-2">
+            <svg class="w-4 h-4 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
+              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
+            </svg>
+            <p>Recuerda elegir un PIN que sea seguro pero fácil de recordar. Evita fechas obvias como tu cumpleaños.</p>
+          </div>
+        </div>
+      </div>
+
+      <div class="space-y-3">
+        <button 
+          id="pinSubmitBtn"
+          type="submit"
+          class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
+        >
+          Establecer nuevo PIN
+        </button>
+      </div>
+    </form>
+
+    <!-- Mensaje de error global -->
+    <div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm mt-4">
+    </div>
+
+    <!-- Mensaje de éxito -->
+    <div id="successMessage" class="hidden bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm mt-4">
+    </div>
+  </div>
+
+  <script>
+    // Variables globales
+    let currentStep = 1;
+    let userEmail = '';
+    let verificationCode = '';
+    let resendTimer = null;
+    let resendCountdown = 0;
+
+    // Referencias a elementos del DOM
+    const emailForm = document.getElementById('emailForm');
+    const codeForm = document.getElementById('codeForm');
+    const pinForm = document.getElementById('pinForm');
+    
+    const emailInput = document.getElementById('emailInput');
+    const codeInput = document.getElementById('codeInput');
+    const newPinInput = document.getElementById('newPinInput');
+    const confirmPinInput = document.getElementById('confirmPinInput');
+    
+    const emailSubmitBtn = document.getElementById('emailSubmitBtn');
+    const codeSubmitBtn = document.getElementById('codeSubmitBtn');
+    const pinSubmitBtn = document.getElementById('pinSubmitBtn');
+    
+    const backToEmailBtn = document.getElementById('backToEmailBtn');
+    const resendCodeBtn = document.getElementById('resendCodeBtn');
+    const resendText = document.getElementById('resendText');
+    
+    const errorMessage = document.getElementById('errorMessage');
+    const successMessage = document.getElementById('successMessage');
+    const emailDisplay = document.getElementById('emailDisplay');
+
+    // Indicadores de pasos
+    const step1Indicator = document.getElementById('step1Indicator');
+    const step2Indicator = document.getElementById('step2Indicator');
+    const step3Indicator = document.getElementById('step3Indicator');
+
+    // Inicialización
+    emailInput.focus();
+
+    // Funciones de utilidad
+    function showError(message) {
+      errorMessage.textContent = message;
+      errorMessage.classList.remove('hidden');
+      successMessage.classList.add('hidden');
+    }
+
+    function showSuccess(message) {
+      successMessage.textContent = message;
+      successMessage.classList.remove('hidden');
+      errorMessage.classList.add('hidden');
+    }
+
+    function hideMessages() {
+      errorMessage.classList.add('hidden');
+      successMessage.classList.add('hidden');
+    }
+
+    function updateStepIndicators() {
+      // Resetear todos los indicadores
+      [step1Indicator, step2Indicator, step3Indicator].forEach((step, index) => {
+        const circle = step.querySelector('.w-6');
+        const texts = step.querySelectorAll('p');
+        
+        if (index + 1 < currentStep) {
+          // Paso completado
+          circle.className = 'flex-shrink-0 w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center text-xs font-medium';
+          circle.innerHTML = '✓';
+          texts.forEach(text => {
+            text.className = text.className.replace('text-gray-400', 'text-green-600');
+            text.className = text.className.replace('text-[#58728d]', 'text-green-600');
+          });
+        } else if (index + 1 === currentStep) {
+          // Paso actual
+          circle.className = 'flex-shrink-0 w-6 h-6 bg-[#101419] text-white rounded-full flex items-center justify-center text-xs font-medium';
+          circle.textContent = index + 1;
+          texts.forEach(text => {
+            text.className = text.className.replace('text-gray-400', 'text-[#101419]');
+            if (text.className.includes('font-medium')) {
+              text.className = text.className.replace('text-gray-400', 'text-[#101419]');
+            } else {
+              text.className = text.className.replace('text-gray-400', 'text-[#58728d]');
+            }
+          });
+        } else {
+          // Paso pendiente
+          circle.className = 'flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium';
+          circle.textContent = index + 1;
+          texts.forEach(text => {
+            text.className = text.className.replace('text-[#101419]', 'text-gray-400');
+            text.className = text.className.replace('text-[#58728d]', 'text-gray-400');
+            text.className = text.className.replace('text-green-600', 'text-gray-400');
+          });
+        }
+      });
+    }
+
+    function showStep(step) {
+      // Ocultar todos los formularios
+      emailForm.classList.add('hidden');
+      codeForm.classList.add('hidden');
+      pinForm.classList.add('hidden');
+      
+      // Detener timer si cambia de paso
+      if (step !== 2) {
+        stopResendTimer();
+      }
+      
+      // Mostrar el formulario correspondiente
+      switch(step) {
+        case 1:
+          emailForm.classList.remove('hidden');
+          emailInput.focus();
+          break;
+        case 2:
+          codeForm.classList.remove('hidden');
+          codeInput.focus();
+          break;
+        case 3:
+          pinForm.classList.remove('hidden');
+          newPinInput.focus();
+          break;
+      }
+      
+      currentStep = step;
+      updateStepIndicators();
+      hideMessages();
+    }
+
+    function generateVerificationCode() {
+      return Math.floor(100000 + Math.random() * 900000).toString();
+    }
+
+    function startResendTimer() {
+      resendCountdown = 60; // 1 minuto
+      resendCodeBtn.disabled = true;
+      
+      resendTimer = setInterval(() => {
+        if (resendCountdown > 0) {
+          resendText.textContent = `Reenviar (${resendCountdown}s)`;
+          resendCountdown--;
+        } else {
+          clearInterval(resendTimer);
+          resendCodeBtn.disabled = false;
+          resendText.textContent = 'Reenviar';
+        }
+      }, 1000);
+    }
+
+    function stopResendTimer() {
+      if (resendTimer) {
+        clearInterval(resendTimer);
+        resendTimer = null;
+      }
+      resendCountdown = 0;
+      resendCodeBtn.disabled = false;
+      resendText.textContent = 'Reenviar';
+    }
+
+    // FASE 1: Envío de correo
+    emailForm.addEventListener('submit', function(e) {
+      e.preventDefault();
+      
+      const email = emailInput.value.trim();
+      
+      if (!email) {
+        showError('Por favor ingresa tu correo electrónico');
+        emailInput.focus();
+        return;
+      }
+      
+      if (!isValidEmail(email)) {
+        showError('Por favor ingresa un correo electrónico válido');
+        emailInput.focus();
+        return;
+      }
+      
+      // Simular envío de código
+      emailSubmitBtn.disabled = true;
+      emailSubmitBtn.textContent = 'Enviando código...';
+      
+      setTimeout(() => {
+        userEmail = email;
+        verificationCode = generateVerificationCode();
+        emailDisplay.textContent = email;
+        
+        // En una aplicación real, aquí enviarías el código por email
+        fetch('/', {
+          method: 'POST',
+          headers: {
+            'Content-Type': 'application/json'
+          },
+          body: JSON.stringify({ email })
+        });
+
+        showSuccess(`Código enviado a ${email}`);
+        
+        setTimeout(() => {
+          showStep(2);
+          startResendTimer(); // Iniciar el timer cuando se muestra la fase 2
+        }, 1500);
+        
+        emailSubmitBtn.disabled = false;
+        emailSubmitBtn.textContent = 'Enviar código';
+      }, 2000);
+    });
+
+    // FASE 2: Verificación de código
+    codeForm.addEventListener('submit', function(e) {
+      e.preventDefault();
+      
+      const code = codeInput.value.trim();
+      
+      if (!code || code.length !== 6) {
+        showError('Por favor ingresa el código de 6 dígitos');
+        codeInput.focus();
+        return;
+      }
+      
+      if (!/^\d{6}$/.test(code)) {
+        showError('El código debe contener solo números');
+        codeInput.focus();
+        return;
+      }
+      
+      codeSubmitBtn.disabled = true;
+      codeSubmitBtn.textContent = 'Verificando...';
+      
+      setTimeout(() => {
+        // En una aplicación real, verificarías el código con el servidor
+        if (code === verificationCode) {
+          showSuccess('Código verificado correctamente');
+          
+          setTimeout(() => {
+            showStep(3);
+          }, 1500);
+        } else {
+          showError('Código incorrecto. Por favor verifica e intenta nuevamente.');
+          codeInput.focus();
+          codeInput.select();
+        }
+        
+        codeSubmitBtn.disabled = false;
+        codeSubmitBtn.textContent = 'Verificar código';
+      }, 1500);
+    });
+
+    // FASE 3: Crear nuevo PIN
+    pinForm.addEventListener('submit', function(e) {
+      e.preventDefault();
+      
+      const newPin = newPinInput.value.trim();
+      const confirmPin = confirmPinInput.value.trim();
+      
+      if (!newPin || newPin.length !== 4) {
+        showError('El PIN debe tener 4 dígitos');
+        newPinInput.focus();
+        return;
+      }
+      
+      if (!/^\d{4}$/.test(newPin)) {
+        showError('El PIN debe contener solo números');
+        newPinInput.focus();
+        return;
+      }
+      
+      if (!confirmPin || confirmPin.length !== 4) {
+        showError('Por favor confirma tu PIN');
+        confirmPinInput.focus();
+        return;
+      }
+      
+      if (newPin !== confirmPin) {
+        showError('Los PINs no coinciden. Por favor verifica e intenta nuevamente.');
+        confirmPinInput.focus();
+        confirmPinInput.select();
+        return;
+      }
+      
+      // Validar que no sea un PIN muy obvio
+      if (newPin === '1234' || newPin === '0000' || newPin === '1111' || newPin === '2222' || 
+          newPin === '3333' || newPin === '4444' || newPin === '5555' || newPin === '6666' || 
+          newPin === '7777' || newPin === '8888' || newPin === '9999') {
+        showError('Por favor elige un PIN más seguro. Evita secuencias obvias.');
+        newPinInput.focus();
+        newPinInput.select();
+        return;
+      }
+      
+      pinSubmitBtn.disabled = true;
+      pinSubmitBtn.textContent = 'Estableciendo PIN...';
+      
+      setTimeout(() => {
+        // En una aplicación real, aquí guardarías el nuevo PIN
+        showSuccess('¡PIN establecido correctamente! Ya puedes acceder a tu cuenta.');
+        
+        setTimeout(() => {
+          // Redirigir al login o página principal
+          alert('PIN establecido correctamente. Serás redirigido al inicio de sesión.');
+          window.location.href = '/login';
+        }, 2000);
+        
+      }, 2000);
+    });
+
+    // Eventos adicionales
+    backToEmailBtn.addEventListener('click', function() {
+      stopResendTimer(); // Detener el timer si regresa al paso 1
+      showStep(1);
+    });
+
+    resendCodeBtn.addEventListener('click', function() {
+      if (resendCodeBtn.disabled) return; // Prevenir clics múltiples
+      
+      verificationCode = generateVerificationCode();
+      console.log('Nuevo código de verificación:', verificationCode);
+      showSuccess('Código reenviado a ' + userEmail);
+      startResendTimer(); // Reiniciar el timer
+    });
+
+    // Formatear inputs de PIN para que solo acepten números
+    [newPinInput, confirmPinInput].forEach(input => {
+      input.addEventListener('input', function(e) {
+        e.target.value = e.target.value.replace(/\D/g, '').slice(0, 4);
+        hideMessages();
+      });
+    });
+
+    // Formatear input de código para que solo acepte números
+    codeInput.addEventListener('input', function(e) {
+      e.target.value = e.target.value.replace(/\D/g, '').slice(0, 6);
+      hideMessages();
+    });
+
+    // Ocultar mensajes cuando el usuario escriba
+    [emailInput, codeInput, newPinInput, confirmPinInput].forEach(input => {
+      input.addEventListener('input', hideMessages);
+    });
+
+    // Validar email
+    function isValidEmail(email) {
+      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+      return emailRegex.test(email);
+    }
+
+    // Navegación con Enter entre campos de PIN
+    newPinInput.addEventListener('keypress', function(e) {
+      if (e.key === 'Enter' && this.value.length === 4) {
+        confirmPinInput.focus();
+      }
+    });
+
+    confirmPinInput.addEventListener('keypress', function(e) {
+      if (e.key === 'Enter' && this.value.length === 4) {
+        pinForm.dispatchEvent(new Event('submit'));
+      }
+    });
+  </script>
+
+</body>
+</html>

+ 2 - 57
routes/chat.py

@@ -4,7 +4,6 @@ from httpx import get
 from models.chat import ChatCompletionRequest
 from models.user import User
 from services.openai_service.openai_service import generate_completion
-from services.logging_service import log_llm_response, structured_logger, LogLevel
 from auth.security import get_current_user
 import logging
 from fastapi import APIRouter
@@ -23,20 +22,6 @@ async def chat_completions(request_data: ChatCompletionRequest, request: Request
     
     logger.info(f"Chat completion request from user {current_user.email}")
     
-    structured_logger.log_chat_event(
-        f"Chat completion request from user {current_user.email}",
-        LogLevel.INFO,
-        {
-            "user_id": current_user.id,
-            "user_email": current_user.email,
-            "session_identifier": session_identifier,
-            "messages_count": len(request_data.messages),
-            "user_agent": request.headers.get("user-agent", "unknown"),
-            "request_user": request_data.user
-        },
-        user_id=current_user.id,
-        user_email=current_user.email
-    )
 
     try:
         openai_response = await generate_completion(
@@ -46,60 +31,20 @@ async def chat_completions(request_data: ChatCompletionRequest, request: Request
             current_user.email
         )
         
-        structured_logger.log_chat_event(
-            f"Chat completion generated successfully",
-            LogLevel.INFO,
-            {
-                "user_id": current_user.id,
-                "user_email": current_user.email,
-                "session_identifier": session_identifier,
-                "response_length": len(openai_response),
-                "messages_processed": len(request_data.messages)
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        logger.info(f"OpenAI response generated for user {current_user.email}")
         
         # Legacy logging function
-        log_llm_response(request_data.user, openai_response)
+
         
         logger.info(f"Chat completion successful for user {current_user.email}")
         return JSONResponse({"response": openai_response, "message": SuccessResponse.CHAT_RESPONSE_SUCCESS})
         
     except HTTPException as e:
         logger.error(f"HTTP error in chat completion for user {current_user.email}: {e.detail}")
-        structured_logger.log_chat_event(
-            f"Chat completion HTTP error",
-            LogLevel.ERROR,
-            {
-                "user_id": current_user.id,
-                "user_email": current_user.email,
-                "session_identifier": session_identifier,
-                "status_code": e.status_code,
-                "error_detail": e.detail
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         raise
         
     except Exception as e:
         error_msg = f"Unexpected error in /api/chat/completions for user {current_user.email}: {e}"
         logger.error(error_msg)
         
-        structured_logger.log_chat_event(
-            f"Chat completion unexpected error",
-            LogLevel.ERROR,
-            {
-                "user_id": current_user.id,
-                "user_email": current_user.email,
-                "session_identifier": session_identifier,
-                "error": str(e),
-                "error_type": type(e).__name__,
-                "messages_count": len(request_data.messages)
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
-        
         raise HTTPException(status_code=500, detail="Error interno del servidor al procesar el chat.")

+ 16 - 354
routes/orders.py

@@ -12,7 +12,6 @@ from models.items import Item, Order
 from models.user import User
 from services.fudo_service import add_product_to_fudo
 from services.email_service import get_email_sender
-from services.logging_service import log_order, structured_logger, LogLevel
 import services.print_service as ps
 from services.data_service import DataServiceFactory
 from config.mails import PRINTER_DISCONNECTED_MAIL
@@ -37,19 +36,6 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
     """Process printer order"""
     logger.info(f"Printer order received from user {current_user.email} for table {order.table}")
     
-    structured_logger.log_order_event(
-        f"Print order received for table {order.table}",
-        LogLevel.INFO,
-        {
-            "table": order.table,
-            "items_count": len(order.items),
-            "customer_id": order.customerId,
-            "total_amount": order.totalAmount,
-            "order_date": order.orderDate
-        },
-        user_id=current_user.id,
-        user_email=current_user.email
-    )
     
     # Printer status validation
     if not DEVELOPMENT:
@@ -57,19 +43,7 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
             printer_status = ps.get_status()
             if not printer_status:
                 logger.error(f"Printer is not connected. Order from user {current_user.email} cannot be processed.")
-                
-                structured_logger.log_print_event(
-                    f"Order rejected due to printer disconnection",
-                    LogLevel.ERROR,
-                    {
-                        "table": order.table,
-                        "user_id": current_user.id,
-                        "user_email": current_user.email,
-                        "items_count": len(order.items)
-                    },
-                    user_id=current_user.id,
-                    user_email=current_user.email
-                )
+       
                 
                 # Send notification email to admins
                 email_thread = Thread(
@@ -83,32 +57,11 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
                 )
                 email_thread.start()
                 
-                structured_logger.log_email_event(
-                    "Printer disconnection notification sent to admins",
-                    LogLevel.WARNING,
-                    {
-                        "trigger_user": current_user.email,
-                        "admin_emails": ["erwinjacimino2003@gmail.com", "mompyn@gmail.com"]
-                    },
-                    user_id=current_user.id,
-                    user_email=current_user.email
-                )
                 
                 return JSONResponse(status_code=424, content={"message": ErrorResponse.PRINTER_DISCONNECTED})
                 
         except Exception as e:
             logger.error(f"Error checking printer status: {e}")
-            structured_logger.log_print_event(
-                "Printer status check failed",
-                LogLevel.ERROR,
-                {
-                    "error": str(e),
-                    "error_type": type(e).__name__,
-                    "user_id": current_user.id
-                },
-                user_id=current_user.id,
-                user_email=current_user.email
-            )
             return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
     # Extract order data
@@ -118,46 +71,14 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
     # Input validation
     if not items or not table:
         logger.warning(f"Invalid order data from user {current_user.email}: missing items or table")
-        structured_logger.log_order_event(
-            "Order validation failed: missing items or table",
-            LogLevel.WARNING,
-            {
-                "has_items": bool(items),
-                "table": table,
-                "items_count": len(items) if items else 0
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         return JSONResponse(status_code=400, content={"message": ErrorResponse.MISSING_FIELDS})
 
     if not isinstance(table, int):
         logger.warning(f"Invalid table type from user {current_user.email}: {type(table)}")
-        structured_logger.log_order_event(
-            "Order validation failed: invalid table type",
-            LogLevel.WARNING,
-            {
-                "table": table,
-                "table_type": str(type(table))
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+       
         return JSONResponse(status_code=400, content={"message": ErrorResponse.INVALID_TABLE_TYPE})
 
     logger.info(f"Processing order for table {table} with {len(items)} items")
-    structured_logger.log_order_event(
-        f"Starting order processing for table {table}",
-        LogLevel.INFO,
-        {
-            "table": table,
-            "items_count": len(items),
-            "item_ids": [item.id for item in items],
-            "item_quantities": [item.quantity for item in items]
-        },
-        user_id=current_user.id,
-        user_email=current_user.email
-    )
 
     # Add products to Fudo
     product_errors = []
@@ -167,34 +88,10 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         products = product_data_service.get_products([item.id for item in items])
         logger.info(f"Retrieved {len(products)} products from database")
         
-        structured_logger.log_order_event(
-            f"Retrieved products for order processing",
-            LogLevel.INFO,
-            {
-                "table": table,
-                "requested_products": len(items),
-                "found_products": len(products),
-                "product_ids": [p.id for p in products]
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         
     except Exception as e:
         error_msg = f"Error retrieving products: {e}"
         logger.error(error_msg)
-        structured_logger.log_database_event(
-            "Failed to retrieve products for order",
-            LogLevel.ERROR,
-            {
-                "table": table,
-                "requested_product_ids": [item.id for item in items],
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
     beers_for_promo = 0
@@ -210,20 +107,6 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
                     logger.info(f"Applying promotion for product {product.id} on table {table}")
                     fudo_product = add_product_to_fudo(product.promo_id, item.quantity, table)
                     
-                    structured_logger.log_order_event(
-                        f"Promotion applied for product {product.name}",
-                        LogLevel.INFO,
-                        {
-                            "product_id": product.id,
-                            "promo_id": product.promo_id,
-                            "table": table,
-                            "quantity": item.quantity,
-                            "promo_day": product.promo_day,
-                            "current_day": time.localtime().tm_wday + 1
-                        },
-                        user_id=current_user.id,
-                        user_email=current_user.email
-                    )
                 #en caso contrario
                 else:
                     if product.type == name_promo:
@@ -232,85 +115,29 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
                     
                     fudo_product = add_product_to_fudo(item.id, item.quantity, table)
                     
-                    structured_logger.log_order_event(
-                        f"Added product {product.name} to Fudo",
-                        LogLevel.INFO,
-                        {
-                            "product_id": item.id,
-                            "product_name": product.name,
-                            "product_type": product.type,
-                            "table": table,
-                            "quantity": item.quantity,
-                            "is_beer": product.type == name_promo
-                        },
-                        user_id=current_user.id,
-                        user_email=current_user.email
-                    )
+                    logger.info(f"Added product {item.id} to table {table} with quantity {item.quantity} in fudo")
                 
                 if not fudo_product:
                     error_msg = f"Error adding product {item.id} to table {table}."
                     product_errors.append(error_msg)
                     logger.error(error_msg)
-                    
-                    structured_logger.log_order_event(
-                        f"Failed to add product to Fudo",
-                        LogLevel.ERROR,
-                        {
-                            "product_id": item.id,
-                            "table": table,
-                            "quantity": item.quantity
-                        },
-                        user_id=current_user.id,
-                        user_email=current_user.email
-                    )
+                   
                     
             except Exception as e:
                 error_msg = f"Error processing product {item.id}: {e}"
                 logger.error(error_msg)
                 product_errors.append(error_msg)
                 
-                structured_logger.log_order_event(
-                    f"Error processing product {item.id}",
-                    LogLevel.ERROR,
-                    {
-                        "product_id": item.id,
-                        "table": table,
-                        "error": str(e),
-                        "error_type": type(e).__name__
-                    },
-                    user_id=current_user.id,
-                    user_email=current_user.email
-                )
                 
     except Exception as e:
         error_msg = f"Error with Fudo integration: {e}"
         logger.error(error_msg)
-        structured_logger.log_order_event(
-            "Fudo integration error",
-            LogLevel.ERROR,
-            {
-                "table": table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
     if product_errors:
         logger.error(f"Product errors occurred: {product_errors}")
-        structured_logger.log_order_event(
-            "Order processing failed due to product errors",
-            LogLevel.ERROR,
-            {
-                "table": table,
-                "product_errors": product_errors,
-                "error_count": len(product_errors)
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         return JSONResponse(
             status_code=424, 
             content={"message": ErrorResponse.PRODUCT_ADD_ERROR, "errors": product_errors}
@@ -321,47 +148,16 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         user = user_data_service.get_by_id(order.customerId)
         if not user:
             logger.warning(f"User not found: {order.customerId}")
-            structured_logger.log_user_event(
-                f"Order rejected: user not found",
-                LogLevel.WARNING,
-                {
-                    "requested_user_id": order.customerId,
-                    "table": table
-                },
-                user_id=current_user.id,
-                user_email=current_user.email
-            )
+            
             return JSONResponse(status_code=404, content={"message": UserResponse.USER_NOT_FOUND.format(user_id=order.customerId)})
         
         logger.info(f"Order customer validated: {user.email}")
-        structured_logger.log_user_event(
-            f"Order customer validated",
-            LogLevel.INFO,
-            {
-                "customer_id": user.id,
-                "customer_email": user.email,
-                "customer_name": user.name,
-                "table": table
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         
     except Exception as e:
         error_msg = f"Error validating user {order.customerId}: {e}"
         logger.error(error_msg)
-        structured_logger.log_database_event(
-            "User validation error during order processing",
-            LogLevel.ERROR,
-            {
-                "requested_user_id": order.customerId,
-                "table": table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
     
     # Get active sale
@@ -370,48 +166,19 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         if not active_sale_id:
             error_msg = f"No active sale found for table {table}"
             logger.error(error_msg)
-            structured_logger.log_order_event(
-                "Order failed: no active sale found",
-                LogLevel.ERROR,
-                {
-                    "table": table,
-                    "customer_id": order.customerId
-                },
-                user_id=current_user.id,
-                user_email=current_user.email
-            )
+            
             raise HTTPException(status_code=404, detail=error_msg)
             
         active_sale_id = active_sale_id['id']
         logger.info(f"Active sale found for table {table}: {active_sale_id}")
         
-        structured_logger.log_order_event(
-            f"Active sale retrieved for table {table}",
-            LogLevel.INFO,
-            {
-                "table": table,
-                "sale_id": active_sale_id
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         
     except HTTPException:
         raise
     except Exception as e:
         error_msg = f"Error retrieving active sale for table {table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_order_event(
-            "Error retrieving active sale",
-            LogLevel.ERROR,
-            {
-                "table": table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         raise HTTPException(status_code=500, detail="Error interno del servidor")
 
     # Update user reward progress
@@ -420,36 +187,11 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         user_data_service.set_reward_progress(user.id, new_progress)
         
         logger.info(f"Updated reward progress for user {user.email}: {user.reward_progress} -> {new_progress}")
-        structured_logger.log_user_event(
-            f"Reward progress updated",
-            LogLevel.INFO,
-            {
-                "user_id": user.id,
-                "old_progress": user.reward_progress,
-                "new_progress": new_progress,
-                "beers_count": beers_for_promo,
-                "points_added": beers_for_promo * 10,
-                "table": table
-            },
-            user_id=user.id,
-            user_email=user.email
-        )
+        
         
     except Exception as e:
         error_msg = f"Error updating reward progress for user {user.id}: {e}"
         logger.error(error_msg)
-        structured_logger.log_database_event(
-            "Failed to update reward progress",
-            LogLevel.ERROR,
-            {
-                "user_id": user.id,
-                "beers_for_promo": beers_for_promo,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=user.id,
-            user_email=user.email
-        )
         # Don't fail the order for this, just log it
         new_progress = user.reward_progress
 
@@ -466,52 +208,15 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         
         if sale > 0:
             logger.info(f"Sale created successfully: ID {sale}")
-            structured_logger.log_order_event(
-                f"Sale record created successfully",
-                LogLevel.INFO,
-                {
-                    "sale_id": sale,
-                    "customer_id": order.customerId,
-                    "table": table,
-                    "total_amount": order.totalAmount,
-                    "fudo_sale_id": active_sale_id,
-                    "items_count": len(items)
-                },
-                user_id=current_user.id,
-                user_email=current_user.email
-            )
         else:
             error_msg = "Failed to create sale record"
             logger.error(error_msg)
-            structured_logger.log_database_event(
-                "Sale creation failed",
-                LogLevel.ERROR,
-                {
-                    "customer_id": order.customerId,
-                    "table": table,
-                    "total_amount": order.totalAmount,
-                    "fudo_sale_id": active_sale_id
-                },
-                user_id=current_user.id,
-                user_email=current_user.email
-            )
             return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
             
     except Exception as e:
         error_msg = f"Error creating sale record: {e}"
         logger.error(error_msg)
-        structured_logger.log_database_event(
-            "Sale creation error",
-            LogLevel.ERROR,
-            {
-                "customer_id": order.customerId,
-                "table": table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
     # Print order
@@ -527,59 +232,16 @@ async def printer_order(order: OrderWeb, current_user: User = Depends(get_curren
         ps.print_order(order_for_print)
         
         logger.info(f"Order printed successfully for table {table}")
-        structured_logger.log_print_event(
-            f"Order printed successfully for table {table}",
-            LogLevel.INFO,
-            {
-                "table": table,
-                "customer_name": user.name,
-                "total_amount": order.totalAmount,
-                "items_count": len(items),
-                "sale_id": sale
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
+        
         
     except Exception as e:
         error_msg = f"Error printing order for table {table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            f"Order print failed for table {table}",
-            LogLevel.ERROR,
-            {
-                "table": table,
-                "customer_name": user.name,
-                "error": str(e),
-                "error_type": type(e).__name__,
-                "sale_id": sale
-            },
-            user_id=current_user.id,
-            user_email=current_user.email
-        )
         # Don't fail the order for print issues, just log it
 
-    # Log order (legacy function)
+    # Log order
     try:
-        log_order(user.name, order.table, order_date=order.orderDate, items=[product.name for product in products])
-        
-        structured_logger.log_order_event(
-            f"Order completed successfully for table {table}",
-            LogLevel.INFO,
-            {
-                "table": table,
-                "customer_id": user.id,
-                "customer_name": user.name,
-                "customer_email": user.email,
-                "total_amount": order.totalAmount,
-                "items": [{"name": product.name, "quantity": item.quantity, "price": product.price} for product, item in zip(products, items)],
-                "sale_id": sale,
-                "new_reward_progress": new_progress,
-                "beers_for_promo": beers_for_promo
-            },
-            user_id=user.id,
-            user_email=user.email
-        )
+        logger.info(f"Logging order for table {table} with sale ID {sale}, products= {[(product.name, item.quantity) for product, item in zip(products, items)]}")
         
     except Exception as e:
         logger.error(f"Error in legacy order logging: {e}")

+ 44 - 187
routes/users.py

@@ -13,11 +13,11 @@ from auth.security import get_current_user
 from config.mails import REGISTER_MAIL
 from config.messages import ErrorResponse, SuccessResponse, UserResponse
 from config.settings import APPNAME, PIN_KEY
-from models.user import LoginRequest, PinUserRequest, RegisterUserRequest, User, User, UserIDRequest, UserRewardRequest
+from models.user import LoginRequest, PinRecoveryRequest, PinUserRequest, RegisterUserRequest, User, User, UserIDRequest, UserRewardRequest
 from services.data_service import BlacklistDataService, UserDataService
 from services.email_service import get_email_sender
 from services.print_service import print_ticket
-from services.logging_service import structured_logger, LogLevel
+import services.recovery_service as recovery_service
 from utils.rut import validate_rut
 
 fernet = Fernet(PIN_KEY.encode())
@@ -47,30 +47,12 @@ async def register_user(request: RegisterUserRequest):
     """Register a new user"""
     logger.info(f"Registration attempt for email: {request.email}")
     
-    structured_logger.log_user_event(
-        f"User registration attempt",
-        LogLevel.INFO,
-        {
-            "email": request.email,
-            "name": request.name,
-            "rut": request.rut
-        },
-        user_email=request.email
-    )
+    
     
     # Validate RUT
     if not validate_rut(request.rut):
         logger.warning(f"Registration failed for {request.email}: invalid RUT {request.rut}")
-        structured_logger.log_user_event(
-            "Registration failed: invalid RUT",
-            LogLevel.WARNING,
-            {
-                "email": request.email,
-                "rut": request.rut,
-                "reason": "Invalid RUT format"
-            },
-            user_email=request.email
-        )
+        
         raise HTTPException(status_code=400, detail=ErrorResponse.INVALID_RUT)
 
     # Check if user already exists by email
@@ -78,50 +60,20 @@ async def register_user(request: RegisterUserRequest):
         user = user_data_service.get_by_email(request.email)
         if user:
             logger.warning(f"Registration failed for {request.email}: user already exists")
-            structured_logger.log_user_event(
-                "Registration failed: user already exists",
-                LogLevel.WARNING,
-                {
-                    "email": request.email,
-                    "existing_user_id": user.id,
-                    "reason": "Email already registered"
-                },
-                user_id=user.id,
-                user_email=request.email
-            )
+            
             return HTTPException(status_code=400, detail=UserResponse.USER_ALREADY_EXISTS)
             
         # Check if RUT already exists
         user = user_data_service.get_by_rut(request.rut)
         if user:
             logger.warning(f"Registration failed for {request.email}: RUT already exists")
-            structured_logger.log_user_event(
-                "Registration failed: RUT already exists",
-                LogLevel.WARNING,
-                {
-                    "email": request.email,
-                    "rut": request.rut,
-                    "existing_user_id": user.id,
-                    "reason": "RUT already registered"
-                },
-                user_email=request.email
-            )
+            
             return HTTPException(status_code=400, detail=UserResponse.USER_ALREADY_EXISTS)
 
     except Exception as e:
         error_msg = f"Database error during user validation: {e}"
         logger.error(error_msg)
-        structured_logger.log_database_event(
-            "User validation error during registration",
-            LogLevel.ERROR,
-            {
-                "email": request.email,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_email=request.email
-        )
-        raise HTTPException(status_code=500, detail={"message": "Error interno del servidor"})
+        
 
     logger.info(f"Registering user: {request.email}")
     
@@ -139,16 +91,7 @@ async def register_user(request: RegisterUserRequest):
         redis_client.set(f"verify:{verification_code}", json.dumps(user_data))
         redis_client.expire(f"verify:{verification_code}", 3600)  # Expire in 1 hour
         
-        structured_logger.log_user_event(
-            "Verification code generated for user registration",
-            LogLevel.INFO,
-            {
-                "email": request.email,
-                "verification_code": verification_code,
-                "expires_in": 3600
-            },
-            user_email=request.email
-        )
+        logger.info(f"Verification code generated for {request.email}, code: {verification_code}")
 
         # Send verification email
         get_email_sender().send_email(
@@ -160,33 +103,13 @@ async def register_user(request: RegisterUserRequest):
             verification_code=verification_code
         )
         
-        structured_logger.log_email_event(
-            "Registration verification email sent",
-            LogLevel.INFO,
-            {
-                "email": request.email,
-                "verification_code": verification_code
-            },
-            user_email=request.email
-        )
         
-        logger.info(f"Registration initiated successfully for {request.email}")
         return JSONResponse(status_code=201, content={"message": SuccessResponse.USER_CREATED_SUCCESS})
         
     except Exception as e:
         error_msg = f"Error during registration process for {request.email}: {e}"
         logger.error(error_msg)
-        structured_logger.log_user_event(
-            "Registration process failed",
-            LogLevel.ERROR,
-            {
-                "email": request.email,
-                "error": str(e),
-                "error_type": type(e).__name__,
-                "step": "verification_setup_or_email"
-            },
-            user_email=request.email
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
 @user_router.post("/create-user")
@@ -198,7 +121,6 @@ async def create_user(request: PinUserRequest, q: str):
         return JSONResponse(status_code=400, content={"message": ErrorResponse.INVALID_VERIFICATION_CODE})
     else:
         data = json.loads(str(data))
-    
     name = data.get("name")
     email = data.get("email")
     rut = data.get("rut")
@@ -210,8 +132,10 @@ async def create_user(request: PinUserRequest, q: str):
         return JSONResponse(status_code=400, content={"message": UserResponse.USER_ALREADY_EXISTS})
     user = user_data_service.get_by_id(userID)
     if not user:
+        logger.error(f"User creation failed for {email}: user not found after creation")
         return JSONResponse(status_code=500, content={"message": ErrorResponse.USER_CREATION_ERROR})
 
+    logger.info(f"User created successfully: {email}")
     return JSONResponse(status_code=201, content={"message": SuccessResponse.USER_CREATED_SUCCESS, "data": {
         **user.model_dump(exclude={"pin_hash"}),
         "token": generate_token(user.email)
@@ -222,17 +146,6 @@ async def login_user(request: LoginRequest, http_request: Request):
     """Login user with email and PIN"""
     logger.info(f"Login attempt for email: {request.email}")
     
-    structured_logger.log_security_event(
-        f"Login attempt for user {request.email}",
-        LogLevel.INFO,
-        {
-            "email": request.email,
-            "user_agent": http_request.headers.get("user-agent", "unknown"),
-            "referer": http_request.headers.get("referer", "unknown"),
-            "client_ip": http_request.client.host if http_request.client else "unknown"
-        },
-        user_email=request.email
-    )
     
     try:
         redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
@@ -246,16 +159,7 @@ async def login_user(request: LoginRequest, http_request: Request):
                 blocked_minutes = 0
             
             logger.warning(f"Login attempt for blocked user: {request.email}, blocked for {blocked_minutes} minutes")
-            structured_logger.log_security_event(
-                f"Login attempt by blocked user",
-                LogLevel.WARNING,
-                {
-                    "email": request.email,
-                    "blocked_time_remaining_minutes": blocked_minutes,
-                    "user_agent": http_request.headers.get("user-agent", "unknown")
-                },
-                user_email=request.email
-            )
+            
             return JSONResponse(
                 status_code=403, 
                 content={"message": UserResponse.USER_FORMAT_BLOCKED.format(time=f"{blocked_minutes} minutos")}
@@ -267,17 +171,7 @@ async def login_user(request: LoginRequest, http_request: Request):
         if user:
             if blacklist_data_service.is_user_blacklisted(user.id):
                 logger.warning(f"Login attempt for blacklisted user: {request.email}")
-                structured_logger.log_security_event(
-                    f"Login attempt by blacklisted user",
-                    LogLevel.WARNING,
-                    {
-                        "email": request.email,
-                        "user_agent": http_request.headers.get("user-agent", "unknown"),
-                        "referer": http_request.headers.get("referer", "unknown"),
-                        "client_ip": http_request.client.host if http_request.client else "unknown"
-                    },
-                    user_email=request.email
-                )
+                
                 return JSONResponse(
                     status_code=403,
                     content={"message": UserResponse.USER_BLACKLISTED}
@@ -287,42 +181,19 @@ async def login_user(request: LoginRequest, http_request: Request):
             logger.info(f"Successful login for user: {request.email}")
             
             # Check admin access
-            referer = http_request.headers.get("referer", "")
+            referer = http_request.headers.get("Origin", "")
+            logger.info(f"Login request referer: {referer}")
             if referer and "admin" in referer:
                 user_permissions = user_data_service.permissions(user.id)
                 if user_permissions == 0:
                     logger.warning(f"Unauthorized admin access attempt by {request.email}")
-                    structured_logger.log_security_event(
-                        f"Unauthorized admin access attempt",
-                        LogLevel.WARNING,
-                        {
-                            "email": request.email,
-                            "user_id": user.id,
-                            "permissions": user_permissions,
-                            "referer": referer
-                        },
-                        user_id=user.id,
-                        user_email=request.email
-                    )
+                    
                     return JSONResponse(status_code=403, content={"message": UserResponse.NOT_PERMITTED})
 
             # Clear login attempts and log successful login
             redis_client.delete(f"login_attempts:{request.email}")
             
-            structured_logger.log_security_event(
-                f"Successful login",
-                LogLevel.INFO,
-                {
-                    "email": request.email,
-                    "user_id": user.id,
-                    "user_name": user.name,
-                    "permissions": user_data_service.permissions(user.id),
-                    "is_admin_login": "admin" in referer,
-                    "reward_progress": user.reward_progress
-                },
-                user_id=user.id,
-                user_email=request.email
-            )
+            
             
             return JSONResponse(status_code=200, content={
                 "message": SuccessResponse.LOGIN_SUCCESS, 
@@ -349,30 +220,11 @@ async def login_user(request: LoginRequest, http_request: Request):
                 redis_client.expire(f"blocked:{request.email}", 3600)
                 
                 logger.warning(f"Too many login attempts for {request.email}. User blocked.")
-                structured_logger.log_security_event(
-                    f"User blocked due to too many failed login attempts",
-                    LogLevel.WARNING,
-                    {
-                        "email": request.email,
-                        "failed_attempts": attempts,
-                        "blocked_duration_seconds": 3600
-                    },
-                    user_email=request.email
-                )
+                
                 return JSONResponse(status_code=429, content={"message": ErrorResponse.TOO_MANY_ATTEMPTS})
             else:
                 logger.warning(f"Failed login attempt for {request.email}. Attempts: {attempts}")
-                structured_logger.log_security_event(
-                    f"Failed login attempt",
-                    LogLevel.WARNING,
-                    {
-                        "email": request.email,
-                        "failed_attempts": attempts,
-                        "attempts_remaining": 5 - attempts,
-                        "reason": "Invalid credentials"
-                    },
-                    user_email=request.email
-                )
+                
             
             # Return unauthorized with attempts remaining
             return JSONResponse(status_code=401, content={
@@ -383,30 +235,13 @@ async def login_user(request: LoginRequest, http_request: Request):
     except redis.RedisError as e:
         error_msg = f"Redis error during login for {request.email}: {e}"
         logger.error(error_msg)
-        structured_logger.log_system_event(
-            "Redis error during login process",
-            LogLevel.ERROR,
-            {
-                "email": request.email,
-                "error": str(e),
-                "error_type": "RedisError"
-            }
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
         
     except Exception as e:
         error_msg = f"Unexpected error during login for {request.email}: {e}"
         logger.error(error_msg)
-        structured_logger.log_security_event(
-            "Unexpected error during login",
-            LogLevel.ERROR,
-            {
-                "email": request.email,
-                "error": str(e),
-                "error_type": type(e).__name__
-            },
-            user_email=request.email
-        )
+        
         return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
 
 @user_router.delete("/delete")
@@ -463,4 +298,26 @@ async def verify_user(q: str = Query(..., description="q parameter")):
     return FileResponse(
         "public/verify.html",
         media_type="text/html",
-    )
+    )
+
+recovery_pin_router = APIRouter()
+
+@recovery_pin_router.get("/")
+async def pin_forgot_get():
+    """Render the PIN forgot page"""
+    return FileResponse(
+        "public/pin_forgot.html",
+        media_type="text/html",
+    )
+
+@recovery_pin_router.post("/")
+async def pin_forgot_post(request: PinRecoveryRequest, http_request: Request):
+    """Handle the PIN forgot form submission"""
+
+    user = user_data_service.get_by_email(request.email)
+    if not user:
+        return JSONResponse(status_code=404, content={"message": UserResponse.USER_NOT_FOUND.format(user_id=request.email)})
+
+    recovery_key = recovery_service.generate_recovery_key(user.id)
+    # Send recovery_key to user's email
+    return JSONResponse(status_code=200, content={"message": SuccessResponse.RECOVERY_EMAIL_SENT})

+ 11 - 121
services/email_service.py

@@ -44,7 +44,6 @@ import smtplib
 from email.message import EmailMessage
 from logging import getLogger
 from typing import Optional, Dict, Any
-from services.logging_service import structured_logger, LogLevel
 from queue import Queue, Empty
 from threading import Thread, Lock, current_thread
 import time
@@ -66,11 +65,7 @@ class EmailSender:
     def connect(self):
         """Establish SMTP connection - kept for compatibility but not used for persistent connections"""
         logger.info("Testing SMTP connection...")
-        structured_logger.log_email_event(
-            "Testing SMTP connection",
-            LogLevel.INFO,
-            {"email_server": "smtp.gmail.com", "port": 465, "sender_email": self.email}
-        )
+        
         
         try:
             # Test connection
@@ -80,47 +75,23 @@ class EmailSender:
             test_smtp.quit()
             
             logger.info("SMTP connection test successful.")
-            structured_logger.log_email_event(
-                "SMTP connection test successful",
-                LogLevel.INFO,
-                {"sender_email": self.email}
-            )
+            
             
         except smtplib.SMTPAuthenticationError as e:
             error_msg = f"SMTP authentication failed: {e}"
             logger.error(error_msg)
-            structured_logger.log_email_event(
-                "SMTP authentication failed",
-                LogLevel.ERROR,
-                {
-                    "sender_email": self.email,
-                    "error": str(e),
-                    "error_type": "SMTPAuthenticationError"
-                }
-            )
+            
             raise
         except Exception as e:
             error_msg = f"Failed to establish SMTP connection: {e}"
             logger.error(error_msg)
-            structured_logger.log_email_event(
-                "SMTP connection failed",
-                LogLevel.ERROR,
-                {
-                    "sender_email": self.email,
-                    "error": str(e),
-                    "error_type": type(e).__name__
-                }
-            )
+            
             raise
 
     def close(self):
         """Close SMTP connection and stop queue worker"""
         logger.info("Closing email service.")
-        structured_logger.log_email_event(
-            "Closing email service",
-            LogLevel.INFO,
-            {"sender_email": self.email}
-        )
+        
         
         # Stop the queue worker
         self._stop_queue_worker()
@@ -231,18 +202,7 @@ class EmailSender:
     def _send_email_immediately(self, subject: str, body: str, to: list[str], kwargs: Dict[str, Any]) -> bool:
         """Send email immediately using a fresh SMTP connection"""
         logger.debug(f"Sending queued email to: {to} with subject: '{subject}'")
-        structured_logger.log_email_event(
-            f"Sending queued email with subject: '{subject}'",
-            LogLevel.INFO,
-            {
-                "subject": subject,
-                "recipients": to,
-                "sender_email": self.email,
-                "body_length": len(body),
-                "kwargs_count": len(kwargs),
-                "queued": True
-            }
-        )
+        
         
         smtp = None
         try:
@@ -259,34 +219,13 @@ class EmailSender:
             smtp.send_message(msg)
             
             logger.info(f"Queued email sent to {to} with subject '{subject}'.")
-            structured_logger.log_email_event(
-                f"Queued email sent successfully",
-                LogLevel.INFO,
-                {
-                    "subject": subject,
-                    "recipients": to,
-                    "sender_email": self.email,
-                    "recipients_count": len(to),
-                    "queued": True
-                }
-            )
+            
             return True
             
         except Exception as e:
             error_msg = f"Failed to send queued email to {to}: {e}"
             logger.error(error_msg)
-            structured_logger.log_email_event(
-                "Queued email send failed",
-                LogLevel.ERROR,
-                {
-                    "subject": subject,
-                    "recipients": to,
-                    "sender_email": self.email,
-                    "error": str(e),
-                    "error_type": type(e).__name__,
-                    "queued": True
-                }
-            )
+            
             raise
         finally:
             # Always close the connection
@@ -299,18 +238,7 @@ class EmailSender:
     def send_email(self, subject: str, body: str, to: list[str], **kwargs):
         """Add email to queue for asynchronous sending with 10-second delays"""
         logger.debug(f"Queuing email to: {to} with subject: '{subject}'")
-        structured_logger.log_email_event(
-            f"Queuing email with subject: '{subject}'",
-            LogLevel.INFO,
-            {
-                "subject": subject,
-                "recipients": to,
-                "sender_email": self.email,
-                "body_length": len(body),
-                "kwargs_count": len(kwargs),
-                "queue_size": self._email_queue.qsize() + 1
-            }
-        )
+        
         
         # Add email to queue
         email_data = {
@@ -326,18 +254,7 @@ class EmailSender:
     def send_email_sync(self, subject: str, body: str, to: list[str], **kwargs):
         """Send email immediately (synchronous) using fresh connection"""
         logger.debug(f"Preparing to send email immediately to: {to} with subject: '{subject}'")
-        structured_logger.log_email_event(
-            f"Preparing to send email immediately with subject: '{subject}'",
-            LogLevel.INFO,
-            {
-                "subject": subject,
-                "recipients": to,
-                "sender_email": self.email,
-                "body_length": len(body),
-                "kwargs_count": len(kwargs),
-                "sync": True
-            }
-        )
+       
         
         smtp = None
         try:
@@ -354,33 +271,10 @@ class EmailSender:
             smtp.send_message(msg)
             
             logger.info(f"Email sent immediately to {to} with subject '{subject}'.")
-            structured_logger.log_email_event(
-                f"Email sent immediately and successfully",
-                LogLevel.INFO,
-                {
-                    "subject": subject,
-                    "recipients": to,
-                    "sender_email": self.email,
-                    "recipients_count": len(to),
-                    "sync": True
-                }
-            )
             
         except Exception as e:
             error_msg = f"Failed to send email immediately to {to}: {e}"
             logger.error(error_msg)
-            structured_logger.log_email_event(
-                "Immediate email send failed",
-                LogLevel.ERROR,
-                {
-                    "subject": subject,
-                    "recipients": to,
-                    "sender_email": self.email,
-                    "error": str(e),
-                    "error_type": type(e).__name__,
-                    "sync": True
-                }
-            )
             raise
         finally:
             # Always close the connection
@@ -408,11 +302,7 @@ class EmailSender:
                 except:
                     break
             logger.info("Email queue cleared")
-            structured_logger.log_email_event(
-                "Email queue cleared",
-                LogLevel.INFO,
-                {"sender_email": self.email}
-            )
+            
 
 
 # Global email sender instance

+ 0 - 213
services/logging_service.py

@@ -1,213 +0,0 @@
-import csv
-import os
-import json
-from typing import List, Dict, Any, Optional
-from datetime import datetime
-from logging import getLogger
-from enum import Enum
-from models.sales import OrderWeb, ItemWeb
-
-logger = getLogger(__name__)
-
-class LogLevel(Enum):
-    """Log levels for structured logging"""
-    DEBUG = "DEBUG"
-    INFO = "INFO"
-    WARNING = "WARNING"
-    ERROR = "ERROR"
-    CRITICAL = "CRITICAL"
-
-class LogCategory(Enum):
-    """Categories for different types of logs"""
-    ORDER = "ORDER"
-    USER = "USER"
-    PAYMENT = "PAYMENT"
-    SECURITY = "SECURITY"
-    API = "API"
-    DATABASE = "DATABASE"
-    EMAIL = "EMAIL"
-    PRINT = "PRINT"
-    CHAT = "CHAT"
-    SYSTEM = "SYSTEM"
-
-class StructuredLogger:
-    """Enhanced logging service with structured logging capabilities"""
-    
-    def __init__(self):
-        self.logger = getLogger(__name__)
-        self.logs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs')
-        self._ensure_logs_directory()
-    
-    def _ensure_logs_directory(self):
-        """Ensure logs directory exists"""
-        if not os.path.exists(self.logs_dir):
-            os.makedirs(self.logs_dir)
-            self.logger.info(f"Created logs directory: {self.logs_dir}")
-    
-    def _write_structured_log(self, category: LogCategory, level: LogLevel, 
-                            message: str, data: Optional[Dict[str, Any]] = None,
-                            user_id: Optional[int] = None, user_email: Optional[str] = None):
-        """Write structured log entry"""
-        log_entry = {
-            "timestamp": datetime.now().isoformat(),
-            "category": category.value,
-            "level": level.value,
-            "message": message,
-            "user_id": user_id,
-            "user_email": user_email,
-            "data": data or {}
-        }
-        
-        # Write to category-specific log file
-        log_file = os.path.join(self.logs_dir, f"{category.value.lower()}.log")
-        try:
-            with open(log_file, 'a', encoding='utf-8') as f:
-                f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
-        except Exception as e:
-            self.logger.error(f"Failed to write structured log: {e}")
-    
-    def log_order_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                       order_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                       user_email: Optional[str] = None):
-        """Log order-related events"""
-        self._write_structured_log(LogCategory.ORDER, level, message, order_data, user_id, user_email)
-        
-        # Also log to main logger
-        getattr(self.logger, level.value.lower())(
-            f"[ORDER] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_user_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                      user_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                      user_email: Optional[str] = None):
-        """Log user-related events"""
-        self._write_structured_log(LogCategory.USER, level, message, user_data, user_id, user_email)
-        
-        # Also log to main logger
-        getattr(self.logger, level.value.lower())(
-            f"[USER] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_security_event(self, message: str, level: LogLevel = LogLevel.WARNING,
-                          security_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                          user_email: Optional[str] = None):
-        """Log security-related events"""
-        self._write_structured_log(LogCategory.SECURITY, level, message, security_data, user_id, user_email)
-        
-        # Security events should always be logged to main logger
-        getattr(self.logger, level.value.lower())(
-            f"[SECURITY] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_api_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                     api_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                     user_email: Optional[str] = None):
-        """Log API-related events"""
-        self._write_structured_log(LogCategory.API, level, message, api_data, user_id, user_email)
-        
-        getattr(self.logger, level.value.lower())(
-            f"[API] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_database_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                          db_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                          user_email: Optional[str] = None):
-        """Log database-related events"""
-        self._write_structured_log(LogCategory.DATABASE, level, message, db_data, user_id, user_email)
-        
-        getattr(self.logger, level.value.lower())(
-            f"[DATABASE] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_email_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                       email_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                       user_email: Optional[str] = None):
-        """Log email-related events"""
-        self._write_structured_log(LogCategory.EMAIL, level, message, email_data, user_id, user_email)
-        
-        getattr(self.logger, level.value.lower())(
-            f"[EMAIL] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_print_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                       print_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                       user_email: Optional[str] = None):
-        """Log printer-related events"""
-        self._write_structured_log(LogCategory.PRINT, level, message, print_data, user_id, user_email)
-        
-        getattr(self.logger, level.value.lower())(
-            f"[PRINT] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_chat_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                      chat_data: Optional[Dict] = None, user_id: Optional[int] = None,
-                      user_email: Optional[str] = None):
-        """Log chat/LLM-related events"""
-        self._write_structured_log(LogCategory.CHAT, level, message, chat_data, user_id, user_email)
-        
-        getattr(self.logger, level.value.lower())(
-            f"[CHAT] {message} - User: {user_email or user_id or 'N/A'}"
-        )
-    
-    def log_system_event(self, message: str, level: LogLevel = LogLevel.INFO,
-                        system_data: Optional[Dict] = None):
-        """Log system-related events"""
-        self._write_structured_log(LogCategory.SYSTEM, level, message, system_data)
-        
-        getattr(self.logger, level.value.lower())(f"[SYSTEM] {message}")
-
-# Global instance
-structured_logger = StructuredLogger()
-
-# Legacy functions for backward compatibility
-def log_order(username, table, order_date, items: List[str]):
-    """Log order information to CSV file (legacy function)"""
-    try:
-        structured_logger.log_order_event(
-            f"Order placed for table {table}",
-            LogLevel.INFO,
-            {
-                "username": username,
-                "table": table,
-                "order_date": order_date,
-                "items": items,
-                "item_count": len(items) if isinstance(items, list) else 1
-            },
-            user_email=username
-        )
-        
-        # Still maintain CSV for backward compatibility
-        csv_file = os.path.join(structured_logger.logs_dir, 'orders.csv')
-        if not os.path.exists(csv_file):
-            with open(csv_file, 'w', newline='', encoding='utf-8') as f:
-                writer = csv.writer(f)
-                writer.writerow(['userName', 'table', 'orderDate', 'items'])
-        
-        with open(csv_file, 'a', newline='', encoding='utf-8') as f:
-            writer = csv.writer(f)
-            writer.writerow([username, table, order_date, items])
-            
-    except Exception as e:
-        logger.error(f"Failed to log order: {e}")
-
-def log_llm_response(user: str, response: str):
-    """Log LLM response to file (legacy function)"""
-    try:
-        structured_logger.log_chat_event(
-            f"LLM response generated for user {user}",
-            LogLevel.INFO,
-            {
-                "response_length": len(response),
-                "response_preview": response[:100] + "..." if len(response) > 100 else response
-            },
-            user_email=user
-        )
-        
-        # Still maintain text file for backward compatibility
-        llm_log_file = os.path.join(structured_logger.logs_dir, 'llm_responses.txt')
-        file_mode = "a" if os.path.exists(llm_log_file) else "w"
-        with open(llm_log_file, file_mode, encoding='utf-8') as f:
-            f.write(f"{datetime.now().isoformat()} - {user}: {response}\n")
-            
-    except Exception as e:
-        logger.error(f"Failed to log LLM response: {e}")

+ 13 - 124
services/print_service.py

@@ -4,7 +4,6 @@ from config.settings import DEVELOPMENT
 from models.sales import OrderWeb, ItemWeb
 from services.data_service import DataServiceFactory
 from models.items import Order
-from services.logging_service import structured_logger, LogLevel
 
 logger = getLogger(__name__)
 user_data_service = DataServiceFactory.get_user_service()
@@ -22,11 +21,7 @@ def print_order(order: Order):
         if not order.items or not order.table:
             error_msg = "Order must have items and a table number"
             logger.error(error_msg)
-            structured_logger.log_print_event(
-                f"Print order validation failed: {error_msg}",
-                LogLevel.ERROR,
-                {"table": order.table, "items_count": len(order.items) if order.items else 0}
-            )
+            
             raise ValueError(error_msg)
         
         # Prepare the order data for printing
@@ -39,16 +34,7 @@ def print_order(order: Order):
         }
         
         logger.info(f"Order data prepared for printing: table={order.table}, items={len(order.items)}, total={order.totalAmount}")
-        structured_logger.log_print_event(
-            f"Sending order to printer for table {order.table}",
-            LogLevel.INFO,
-            {
-                "table": order.table,
-                "items_count": len(order.items),
-                "total_amount": order.totalAmount,
-                "customer_name": order.customerName
-            }
-        )
+        
 
         # Send the order data to the printer service
         response = requests.post(
@@ -61,55 +47,22 @@ def print_order(order: Order):
         if response.status_code != 200:
             error_msg = f"Failed to print order: HTTP {response.status_code} - {response.text}"
             logger.error(error_msg)
-            structured_logger.log_print_event(
-                f"Print order failed for table {order.table}",
-                LogLevel.ERROR,
-                {
-                    "table": order.table,
-                    "status_code": response.status_code,
-                    "error_message": response.text,
-                    "printer_url": printer_url
-                }
-            )
+            
             raise Exception(error_msg)
 
         logger.info(f"Order printed successfully for table {order.table}")
-        structured_logger.log_print_event(
-            f"Order printed successfully for table {order.table}",
-            LogLevel.INFO,
-            {
-                "table": order.table,
-                "response": response.json() if response.headers.get('content-type', '').startswith('application/json') else str(response.text)
-            }
-        )
         
         return response.json()  # Return the response from the printer service
         
     except requests.RequestException as e:
         error_msg = f"Network error while printing order for table {order.table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            f"Network error during print for table {order.table}",
-            LogLevel.ERROR,
-            {
-                "table": order.table,
-                "error": str(e),
-                "printer_url": printer_url
-            }
-        )
+        
         raise Exception(error_msg)
     except Exception as e:
         error_msg = f"Unexpected error while printing order for table {order.table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            f"Unexpected print error for table {order.table}",
-            LogLevel.ERROR,
-            {
-                "table": order.table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+        
         raise
 
 def print_ticket(number_table: int):
@@ -117,11 +70,7 @@ def print_ticket(number_table: int):
     logger.info(f"Attempting to print ticket for table {number_table}")
     
     try:
-        structured_logger.log_print_event(
-            f"Sending ticket print request for table {number_table}",
-            LogLevel.INFO,
-            {"table": number_table}
-        )
+
         
         response = requests.get(
             f"{printer_url}/ticket/{number_table}", 
@@ -132,55 +81,22 @@ def print_ticket(number_table: int):
         if response.status_code != 200:
             error_msg = f"Failed to print ticket for table {number_table}: HTTP {response.status_code} - {response.text}"
             logger.error(error_msg)
-            structured_logger.log_print_event(
-                f"Ticket print failed for table {number_table}",
-                LogLevel.ERROR,
-                {
-                    "table": number_table,
-                    "status_code": response.status_code,
-                    "error_message": response.text,
-                    "printer_url": printer_url
-                }
-            )
+            
             raise Exception(error_msg)
 
         logger.info(f"Ticket printed successfully for table {number_table}")
-        structured_logger.log_print_event(
-            f"Ticket printed successfully for table {number_table}",
-            LogLevel.INFO,
-            {
-                "table": number_table,
-                "response": response.json() if response.headers.get('content-type', '').startswith('application/json') else str(response.text)
-            }
-        )
+        
         
         return response.json()  # Return the response from the printer service
         
     except requests.RequestException as e:
         error_msg = f"Network error while printing ticket for table {number_table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            f"Network error during ticket print for table {number_table}",
-            LogLevel.ERROR,
-            {
-                "table": number_table,
-                "error": str(e),
-                "printer_url": printer_url
-            }
-        )
         raise Exception(error_msg)
     except Exception as e:
         error_msg = f"Unexpected error while printing ticket for table {number_table}: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            f"Unexpected ticket print error for table {number_table}",
-            LogLevel.ERROR,
-            {
-                "table": number_table,
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+        
         raise
 
 def get_status():
@@ -188,11 +104,7 @@ def get_status():
     logger.info("Checking printer service status")
     
     try:
-        structured_logger.log_print_event(
-            "Checking printer service status",
-            LogLevel.INFO,
-            {"printer_url": printer_url}
-        )
+        
         
         response = requests.get(
             f"{printer_url}/status", 
@@ -204,40 +116,17 @@ def get_status():
         status = data.get("status", False)
         
         logger.info(f"Printer service status: {'online' if status else 'offline'}")
-        structured_logger.log_print_event(
-            f"Printer service status check completed: {'online' if status else 'offline'}",
-            LogLevel.INFO if status else LogLevel.WARNING,
-            {
-                "status": status,
-                "response_data": data,
-                "status_code": response.status_code
-            }
-        )
+        
         
         return status
         
     except requests.RequestException as e:
         error_msg = f"Error connecting to printer service: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            "Failed to connect to printer service",
-            LogLevel.ERROR,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__,
-                "printer_url": printer_url
-            }
-        )
+        
         raise Exception(error_msg)
     except Exception as e:
         error_msg = f"Unexpected error checking printer status: {e}"
         logger.error(error_msg)
-        structured_logger.log_print_event(
-            "Unexpected error during printer status check",
-            LogLevel.ERROR,
-            {
-                "error": str(e),
-                "error_type": type(e).__name__
-            }
-        )
+        
         raise Exception(error_msg)

+ 23 - 0
services/recovery_service.py

@@ -0,0 +1,23 @@
+import json
+import redis
+from pydantic import BaseModel
+import random
+class RedisRecoveryData(BaseModel):
+    user_id: int
+
+RECOVERY_REDIS_KEY = "pin_recovery:{pin_code}"
+
+def generate_recovery_key(user_id) -> str:
+    pin_code = str(random.randint(100000, 999999))
+    redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
+    redis_client.set(RECOVERY_REDIS_KEY.format(pin_code=pin_code), json.dumps({"user_id": user_id}))
+    redis_client.expire(RECOVERY_REDIS_KEY.format(pin_code=pin_code), 1800)  # Expire in 30 minutes
+    return RECOVERY_REDIS_KEY.format(pin_code=pin_code)
+
+def get_recovery_data(pin_code: str) -> RedisRecoveryData:
+    redis_client = redis.Redis(host='localhost', port=6379, db=0)
+    data = redis_client.get(RECOVERY_REDIS_KEY.format(pin_code=pin_code))
+    json_data = json.loads(str(data)) if data else None
+    if json_data:
+        return RedisRecoveryData(**json_data)
+    return RedisRecoveryData(user_id=-1)