Coverage for app / api / v1 / users.py: 100%
38 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 17:54 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 17:54 +0000
1import logging
2from fastapi import APIRouter, Depends, HTTPException
3from sqlalchemy import select
4from sqlalchemy.ext.asyncio import AsyncSession
6from app.api.deps import get_db, get_current_user
7from app.models.user import User
8from app.schemas.user import UserRead
9from app.core.security import get_supabase_admin
11router = APIRouter()
12logger = logging.getLogger(__name__)
14async def _get_or_create_user(
15 current_user: dict,
16 db: AsyncSession,
17) -> User:
18 """Ensure the Supabase-authenticated user exists in our database."""
19 result = await db.execute(
20 select(User).where(User.supabase_uid == current_user["supabase_uid"])
21 )
22 user = result.scalar_one_or_none()
24 if not user:
25 user = User(
26 id=current_user["id"],
27 email=current_user["email"],
28 supabase_uid=current_user["supabase_uid"],
29 )
30 db.add(user)
31 await db.commit()
32 await db.refresh(user)
34 return user
36@router.get("/me", response_model=UserRead)
37async def get_current_user_profile(
38 current_user: dict = Depends(get_current_user),
39 db: AsyncSession = Depends(get_db),
40):
41 """Get current user's profile (auto-creates if missing)."""
42 user = await _get_or_create_user(current_user, db)
43 return user
45@router.post("/sync", response_model=UserRead, status_code=200)
46async def sync_user(
47 current_user: dict = Depends(get_current_user),
48 db: AsyncSession = Depends(get_db),
49):
50 """Explicitly sync Supabase user to local DB."""
51 user = await _get_or_create_user(current_user, db)
52 return user
54@router.delete("/me", status_code=204)
55async def delete_current_user(
56 current_user: dict = Depends(get_current_user),
57 db: AsyncSession = Depends(get_db),
58):
59 """Delete user from local database and Supabase Auth."""
60 user = await _get_or_create_user(current_user, db)
62 # 1. Delete local user first (SQLAlchemy cascades delete to monitors & ping logs)
63 await db.delete(user)
64 await db.commit()
66 # 2. Best-effort Supabase Auth cleanup
67 try:
68 supabase = get_supabase_admin()
69 supabase.auth.admin.delete_user(current_user["supabase_uid"])
70 except Exception as e:
71 logger.error(f"Failed to delete Supabase auth user: {e}", exc_info=True)
73 return None