Co-authored-by: MENDOZA BALLARDO GAEL RICARDO <gael-meb123@users.noreply.github.com> Co-authored-by: Azareth-Tr <Azareth-Tr@users.noreply.github.com> modificacion de las vistas principales para el usuario ciudadano, primer avance para el panel admin
166 lines
6.0 KiB
Python
166 lines
6.0 KiB
Python
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"
|
|
|
|
|
|
def _fetch_route_for_citizen(user_id: str) -> str | None:
|
|
"""
|
|
Busca la primera dirección verificada del ciudadano y devuelve su `route_id`.
|
|
Devuelve None si no hay dirección verificada.
|
|
"""
|
|
try:
|
|
res = (
|
|
supabase_admin.table("addresses")
|
|
.select("route_id")
|
|
.eq("user_id", user_id)
|
|
.eq("verified", True)
|
|
.limit(1)
|
|
.maybe_single()
|
|
.execute()
|
|
)
|
|
if res.data and isinstance(res.data, dict):
|
|
return res.data.get("route_id")
|
|
except Exception:
|
|
return None
|
|
|
|
return None
|
|
|
|
|
|
@router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED)
|
|
def register(body: RegisterRequest):
|
|
"""
|
|
Registro por email o teléfono. Usa el admin client para confirmar automáticamente
|
|
sin requerir que el usuario verifique su correo.
|
|
"""
|
|
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.")
|
|
|
|
# Crear usuario con confirmación automática vía service_role (bypasea email confirmation)
|
|
try:
|
|
create_attrs: dict = {"password": body.password}
|
|
if body.email:
|
|
create_attrs["email"] = body.email
|
|
create_attrs["email_confirm"] = True
|
|
else:
|
|
create_attrs["phone"] = body.phone
|
|
create_attrs["phone_confirm"] = True
|
|
|
|
admin_resp = supabase_admin.auth.admin.create_user(create_attrs)
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
if "already registered" in error_msg.lower() or "user already exists" in error_msg.lower() or "already been registered" in error_msg.lower():
|
|
raise HTTPException(status_code=400, detail="El usuario ya está registrado.")
|
|
if "signups are disabled" in error_msg.lower():
|
|
raise HTTPException(status_code=400, detail="El registro de nuevos usuarios está deshabilitado temporalmente.")
|
|
raise HTTPException(status_code=400, detail=error_msg)
|
|
|
|
auth_user = admin_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}")
|
|
|
|
# Iniciar sesión para obtener el JWT
|
|
try:
|
|
if body.email:
|
|
session_resp = supabase.auth.sign_in_with_password(
|
|
{"email": body.email, "password": body.password}
|
|
)
|
|
else:
|
|
session_resp = supabase.auth.sign_in_with_password(
|
|
{"phone": body.phone, "password": body.password}
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Usuario creado pero no se pudo iniciar sesión: {e}")
|
|
|
|
# Guardar dirección inicial si viene en el payload (evita un segundo HTTP call desde Flutter)
|
|
saved_route_id: str | None = None
|
|
if body.address_calle and body.address_colonia:
|
|
try:
|
|
from app.services.simulation import get_colonias
|
|
mapping = get_colonias()
|
|
match = next(
|
|
(c for c in mapping if c.get("colonia", "").lower() == body.address_colonia.lower()),
|
|
None,
|
|
)
|
|
if match:
|
|
addr_data: dict = {
|
|
"user_id": str(auth_user.id),
|
|
"label": body.address_label or "Mi Casa",
|
|
"calle": body.address_calle,
|
|
"colonia": body.address_colonia,
|
|
"route_id": match["routeId"],
|
|
"verified": False,
|
|
}
|
|
if body.address_lat is not None:
|
|
addr_data["lat"] = body.address_lat
|
|
if body.address_lng is not None:
|
|
addr_data["lng"] = body.address_lng
|
|
supabase_admin.table("addresses").insert(addr_data).execute()
|
|
saved_route_id = match["routeId"]
|
|
except Exception as e:
|
|
print(f"[register] No se pudo guardar la dirección inicial: {e}")
|
|
|
|
return TokenResponse(
|
|
access_token=session_resp.session.access_token,
|
|
user_id=str(auth_user.id),
|
|
role=body.role,
|
|
route_id=saved_route_id,
|
|
)
|
|
|
|
|
|
@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
|
|
user_id = str(auth_user.id)
|
|
role = _fetch_role(user_id)
|
|
|
|
route_id = None
|
|
if role == 'citizen':
|
|
route_id = _fetch_route_for_citizen(user_id)
|
|
|
|
return TokenResponse(
|
|
access_token=resp.session.access_token,
|
|
user_id=user_id,
|
|
role=role,
|
|
route_id=route_id,
|
|
)
|