""" Authentication Routes Login, register, logout, and user info """ from fastapi import APIRouter, Depends, HTTPException, status, Response from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session from database.connection import get_db from services.auth import ( create_user, authenticate_user, create_access_token, get_user_by_email, hash_password, verify_password ) from utils.auth_middleware import get_current_user from app import config import re router = APIRouter(prefix="/api/auth", tags=["auth"]) class RegisterRequest(BaseModel): email: EmailStr password: str name: str manager_id: str | None = None class LoginRequest(BaseModel): email: EmailStr password: str class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" class UserResponse(BaseModel): id: str email: str name: str | None manager_id: str | None role: str manager_parking_quota: int | None = None week_start_day: int = 0 # Notification preferences notify_weekly_parking: int = 1 notify_daily_parking: int = 1 notify_daily_parking_hour: int = 8 notify_daily_parking_minute: int = 0 notify_parking_changes: int = 1 @router.post("/register", response_model=TokenResponse) def register(data: RegisterRequest, db: Session = Depends(get_db)): """Register a new user""" if get_user_by_email(db, data.email): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered" ) if len(data.password) < 8: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Password must be at least 8 characters" ) user = create_user( db=db, email=data.email, password=data.password, name=data.name, manager_id=data.manager_id ) token = create_access_token(user.id, user.email) return TokenResponse(access_token=token) @router.post("/login", response_model=TokenResponse) def login(data: LoginRequest, response: Response, db: Session = Depends(get_db)): """Login with email and password""" user = authenticate_user(db, data.email, data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) token = create_access_token(user.id, user.email) response.set_cookie( key="session_token", value=token, httponly=True, max_age=config.ACCESS_TOKEN_EXPIRE_MINUTES * 60, samesite="lax" ) return TokenResponse(access_token=token) @router.post("/logout") def logout(response: Response): """Logout and clear session""" response.delete_cookie("session_token") return {"message": "Logged out"} @router.get("/me", response_model=UserResponse) def get_me(user=Depends(get_current_user)): """Get current user info""" return UserResponse( id=user.id, email=user.email, name=user.name, manager_id=user.manager_id, role=user.role, manager_parking_quota=user.manager_parking_quota, week_start_day=user.week_start_day or 0, notify_weekly_parking=user.notify_weekly_parking if user.notify_weekly_parking is not None else 1, notify_daily_parking=user.notify_daily_parking if user.notify_daily_parking is not None else 1, notify_daily_parking_hour=user.notify_daily_parking_hour if user.notify_daily_parking_hour is not None else 8, notify_daily_parking_minute=user.notify_daily_parking_minute if user.notify_daily_parking_minute is not None else 0, notify_parking_changes=user.notify_parking_changes if user.notify_parking_changes is not None else 1 ) @router.get("/holidays/{year}") def get_holidays(year: int): """Get public holidays for a given year""" from services.holidays import get_holidays_for_year if year < 2000 or year > 2100: raise HTTPException(status_code=400, detail="Year must be between 2000 and 2100") return get_holidays_for_year(year)