Normalize email input by stripping whitespace and converting to lowercase in login endpoint, remove EmailStr validation in favor of plain string type, move dark mode class toggle from Layout to App component for better initialization, add theme toggle button to login page, and set dark mode as default theme when no preference is stored
45 lines
1.7 KiB
Python
45 lines
1.7 KiB
Python
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import CurrentUser
|
|
from app.core.security import create_access_token, create_refresh_token, verify_password
|
|
from app.db.session import get_db
|
|
from app.models.domain import User
|
|
from app.schemas.domain import LoginRequest, TokenPair, UserRead
|
|
from app.services.audit import write_audit
|
|
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
|
|
|
|
@router.post("/login", response_model=TokenPair)
|
|
def login(payload: LoginRequest, db: Session = Depends(get_db)) -> TokenPair:
|
|
email = payload.email.strip().lower()
|
|
user = db.scalar(select(User).where(User.email == email))
|
|
if not user or not verify_password(payload.password, user.password_hash):
|
|
if user:
|
|
user.failed_login_attempts += 1
|
|
db.commit()
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
|
permissions = sorted({permission for role in user.roles for permission in role.permissions})
|
|
user.failed_login_attempts = 0
|
|
db.commit()
|
|
write_audit(db, action="login", object_type="user", object_id=user.id, user_id=user.id)
|
|
return TokenPair(
|
|
access_token=create_access_token(user.id, permissions),
|
|
refresh_token=create_refresh_token(user.id),
|
|
)
|
|
|
|
|
|
@router.get("/me", response_model=UserRead)
|
|
def me(user: CurrentUser) -> User:
|
|
return user
|
|
|
|
|
|
@router.post("/logout")
|
|
def logout(user: CurrentUser, db: Session = Depends(get_db)) -> dict[str, str]:
|
|
write_audit(db, action="logout", object_type="user", object_id=user.id, user_id=user.id)
|
|
return {"status": "ok", "at": datetime.utcnow().isoformat()}
|