from fastapi import APIRouter, HTTPException, status from app.schemas.auth import RegisterRequest, LoginRequest, TokenResponse from app.core.supabase_client import supabase, supabase_admin router = APIRouter(prefix="/auth", tags=["auth"]) def _fetch_role(user_id: str) -> str: result = ( supabase_admin.table("users") .select("role") .eq("id", user_id) .maybe_single() .execute() ) return result.data["role"] if result.data else "citizen" @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED) def register(body: RegisterRequest): """ Registro por email o teléfono. - Email: flujo estándar Supabase email+password. - Teléfono: requiere que Supabase tenga configurado un proveedor SMS (Twilio). """ if not body.email and not body.phone: raise HTTPException(status_code=400, detail="Se requiere email o teléfono") if len(body.password) < 6: raise HTTPException(status_code=400, detail="La contraseña debe tener al menos 6 caracteres.") try: if body.email: resp = supabase.auth.sign_up({"email": body.email, "password": body.password}) else: resp = supabase.auth.sign_up({"phone": body.phone, "password": body.password}) except Exception as e: error_msg = str(e) if "already registered" in error_msg.lower() or "user already exists" in error_msg.lower(): raise HTTPException(status_code=400, detail="El usuario ya está registrado en el sistema de autenticación.") if "signups are disabled" in error_msg.lower(): raise HTTPException(status_code=400, detail="El registro de nuevos usuarios está deshabilitado temporalmente.") if "rate limit" in error_msg.lower(): raise HTTPException(status_code=400, detail="Límite de registros excedido por seguridad. Desactiva la confirmación de correos en Supabase o intenta más tarde.") raise HTTPException(status_code=400, detail=error_msg) auth_user = resp.user if not auth_user: raise HTTPException(status_code=400, detail="No se pudo crear el usuario en Supabase Auth") # Crear entrada en public.users con el rol elegido try: supabase_admin.table("users").upsert( { "id": str(auth_user.id), "role": body.role, } ).execute() except Exception as e: raise HTTPException(status_code=500, detail=f"Error al guardar el usuario: {e}") # Si no hubo sesión (email confirmation pendiente) if not resp.session: raise HTTPException( status_code=400, detail="Cuenta creada. Revisa tu correo para confirmar tu email antes de iniciar sesión.", ) return TokenResponse( access_token=resp.session.access_token, user_id=str(auth_user.id), role=body.role, ) @router.post("/login", response_model=TokenResponse) def login(body: LoginRequest): """Login por email o teléfono; devuelve JWT de Supabase.""" if not body.email and not body.phone: raise HTTPException(status_code=400, detail="Se requiere email o teléfono") try: if body.email: resp = supabase.auth.sign_in_with_password( {"email": body.email, "password": body.password} ) else: resp = supabase.auth.sign_in_with_password( {"phone": body.phone, "password": body.password} ) except Exception: raise HTTPException(status_code=401, detail="Credenciales inválidas") auth_user = resp.user role = _fetch_role(str(auth_user.id)) return TokenResponse( access_token=resp.session.access_token, user_id=str(auth_user.id), role=role, )