Automated Action 4e361e2d61 Add user authentication and user-specific invoice management
- Create User model and database schema
- Add JWT authentication with secure password hashing
- Create authentication endpoints for registration and login
- Update invoice routes to require authentication
- Ensure users can only access their own invoices
- Update documentation in README.md
2025-05-30 09:00:26 +00:00

110 lines
3.1 KiB
Python

from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app.api.deps import get_current_user
from app.core.config import settings
from app.core.database import get_db
from app.core.security import (
create_access_token,
get_password_hash,
verify_password,
)
from app.models.user import User
from app.schemas.token import Token
from app.schemas.user import User as UserSchema
from app.schemas.user import UserCreate
router = APIRouter()
@router.post("/register", response_model=UserSchema)
def register_user(
user_in: UserCreate,
db: Session = Depends(get_db),
) -> Any:
"""
Register a new user.
"""
# Check if user with this email already exists
user = db.query(User).filter(User.email == user_in.email).first()
if user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="A user with this email already exists.",
)
# Check if username already exists
user = db.query(User).filter(User.username == user_in.username).first()
if user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="A user with this username already exists.",
)
# Create new user
db_user = User(
email=user_in.email,
username=user_in.username,
hashed_password=get_password_hash(user_in.password),
full_name=user_in.full_name,
is_active=True,
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.post("/login", response_model=Token)
def login_for_access_token(
db: Session = Depends(get_db),
form_data: OAuth2PasswordRequestForm = Depends(),
) -> Any:
"""
OAuth2 compatible token login, get an access token for future requests.
"""
# Try to find user by username first
user = db.query(User).filter(User.username == form_data.username).first()
# If not found, try by email
if not user:
user = db.query(User).filter(User.email == form_data.username).first()
# If still not found or wrong password
if not user or not verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# Check if user is active
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Inactive user",
)
# Create access token
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
subject=user.id, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@router.get("/me", response_model=UserSchema)
def read_users_me(
current_user: User = Depends(get_current_user),
) -> Any:
"""
Get current user.
"""
return current_user