from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.crud import crud_user from app.database import get_db from app.dependencies import get_current_user from app.models.user import User from app.schemas.auth import LogoutRequest, RefreshRequest, RegisterResponse, TokenPair, TokenRefreshed from app.schemas.user import UserLogin, UserOut, UserRegister, UserUpdate from app.services.auth_service import ( create_access_token, create_refresh_token, revoke_refresh_token, rotate_refresh_token, verify_password, ) router = APIRouter() @router.post("/register", response_model=RegisterResponse, status_code=status.HTTP_201_CREATED) async def register(data: UserRegister, db: AsyncSession = Depends(get_db)): if await crud_user.get_by_email(db, data.email): raise HTTPException(status_code=400, detail="Email already registered") user = await crud_user.create(db, data) # Members are auto-approved — issue tokens immediately so they can log in right away if user.role == "member": access_token = create_access_token(user.id) refresh_token = await create_refresh_token(db, user.id) return RegisterResponse( user=UserOut.model_validate(user), access_token=access_token, refresh_token=refresh_token, ) # Organizers must wait for admin approval return RegisterResponse(user=UserOut.model_validate(user)) @router.post("/login", response_model=TokenPair) async def login(data: UserLogin, db: AsyncSession = Depends(get_db)): user = await crud_user.get_by_email(db, data.email) if not user or not verify_password(data.password, user.hashed_password): raise HTTPException(status_code=401, detail="Invalid credentials") access_token = create_access_token(user.id) refresh_token = await create_refresh_token(db, user.id) return TokenPair( access_token=access_token, refresh_token=refresh_token, user=UserOut.model_validate(user), ) @router.post("/refresh", response_model=TokenRefreshed) async def refresh(data: RefreshRequest, db: AsyncSession = Depends(get_db)): result = await rotate_refresh_token(db, data.refresh_token) if result is None: raise HTTPException(status_code=401, detail="Invalid or expired refresh token") new_refresh, user_id = result access_token = create_access_token(user_id) return TokenRefreshed(access_token=access_token, refresh_token=new_refresh) @router.post("/logout", status_code=status.HTTP_204_NO_CONTENT) async def logout(data: LogoutRequest, db: AsyncSession = Depends(get_db)): await revoke_refresh_token(db, data.refresh_token) @router.get("/me", response_model=UserOut) async def me(current_user: User = Depends(get_current_user)): return current_user @router.patch("/me", response_model=UserOut) async def update_me( data: UserUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await crud_user.update(db, current_user, data)