from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session from ..auth_utils import create_access_token, get_current_user, hash_password, verify_password from ..database import get_db from ..models import SystemConfig, User router = APIRouter() class RegisterRequest(BaseModel): username: str email: str password: str class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" class UserResponse(BaseModel): id: int username: str email: str is_admin: bool = False model_config = {"from_attributes": True} @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED) def register(body: RegisterRequest, db: Session = Depends(get_db)): # Allow registration if no users exist yet (bootstrap), otherwise check config has_users = db.query(User).first() is not None if has_users: cfg = db.query(SystemConfig).filter_by(key="allow_registration").first() if cfg and cfg.value != "true": raise HTTPException(status_code=403, detail="Registration is disabled") if db.query(User).filter(User.username == body.username).first(): raise HTTPException(status_code=400, detail="Username already taken") if db.query(User).filter(User.email == body.email).first(): raise HTTPException(status_code=400, detail="Email already registered") is_first = not has_users user = User( username=body.username, email=body.email, hashed_password=hash_password(body.password), is_admin=is_first, ) db.add(user) db.commit() db.refresh(user) token = create_access_token({"sub": str(user.id)}) return TokenResponse(access_token=token) @router.post("/login", response_model=TokenResponse) def login(form: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): user = db.query(User).filter(User.username == form.username).first() if not user or not verify_password(form.password, user.hashed_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", ) token = create_access_token({"sub": str(user.id)}) return TokenResponse(access_token=token) @router.get("/me", response_model=UserResponse) def me(current_user: User = Depends(get_current_user)): return current_user