Files
shop/backend/apps/auth/__init__.py
Marek Lenczewski e3e88cc58e wahnsinn vibe
2026-04-16 19:42:06 +02:00

156 lines
4.2 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, EmailStr
from sqlalchemy.orm import Session
from core.db import get_db
from core.events import event_bus
from core.security import (
decode_token,
get_current_user_id,
hash_password,
make_access_token,
make_refresh_token,
verify_password,
)
from .models import User
router = APIRouter()
class RegisterIn(BaseModel):
email: EmailStr
password: str
name: str = ""
class LoginIn(BaseModel):
email: EmailStr
password: str
class TokenOut(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
role: str
user_id: int
class UserOut(BaseModel):
id: int
email: str
name: str
role: str
locale: str
class UpdateMeIn(BaseModel):
name: str | None = None
locale: str | None = None
class ChangePasswordIn(BaseModel):
old_password: str
new_password: str
@router.post("/register", response_model=TokenOut)
def register(body: RegisterIn, db: Session = Depends(get_db)):
if db.query(User).filter_by(email=body.email.lower()).first():
raise HTTPException(400, "Email already registered")
user = User(
email=body.email.lower(),
password_hash=hash_password(body.password),
name=body.name,
role="customer",
)
db.add(user)
db.commit()
db.refresh(user)
event_bus.publish(
"user.registered",
{"user_id": user.id, "email": user.email},
db=db,
)
return TokenOut(
access_token=make_access_token(user.id, user.role),
refresh_token=make_refresh_token(user.id, user.role),
role=user.role,
user_id=user.id,
)
@router.post("/login", response_model=TokenOut)
def login(body: LoginIn, db: Session = Depends(get_db)):
user = db.query(User).filter_by(email=body.email.lower()).first()
if not user or not verify_password(body.password, user.password_hash):
raise HTTPException(401, "Invalid credentials")
event_bus.publish("user.logged_in", {"user_id": user.id}, db=db)
return TokenOut(
access_token=make_access_token(user.id, user.role),
refresh_token=make_refresh_token(user.id, user.role),
role=user.role,
user_id=user.id,
)
class RefreshIn(BaseModel):
refresh_token: str
@router.post("/refresh", response_model=TokenOut)
def refresh(body: RefreshIn, db: Session = Depends(get_db)):
claims = decode_token(body.refresh_token)
if claims.get("type") != "refresh":
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Not a refresh token")
user_id = int(claims["sub"])
user = db.get(User, user_id)
if not user:
raise HTTPException(401, "User gone")
return TokenOut(
access_token=make_access_token(user.id, user.role),
refresh_token=make_refresh_token(user.id, user.role),
role=user.role,
user_id=user.id,
)
@router.get("/me", response_model=UserOut)
def me(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)):
user = db.get(User, user_id)
if not user:
raise HTTPException(404, "User not found")
return UserOut(id=user.id, email=user.email, name=user.name, role=user.role, locale=user.locale)
@router.put("/me", response_model=UserOut)
def update_me(
body: UpdateMeIn,
user_id: int = Depends(get_current_user_id),
db: Session = Depends(get_db),
):
user = db.get(User, user_id)
if not user:
raise HTTPException(404, "User not found")
if body.name is not None:
user.name = body.name
if body.locale is not None:
user.locale = body.locale
db.commit()
return UserOut(id=user.id, email=user.email, name=user.name, role=user.role, locale=user.locale)
@router.post("/change-password")
def change_password(
body: ChangePasswordIn,
user_id: int = Depends(get_current_user_id),
db: Session = Depends(get_db),
):
user = db.get(User, user_id)
if not user or not verify_password(body.old_password, user.password_hash):
raise HTTPException(400, "Old password wrong")
user.password_hash = hash_password(body.new_password)
db.commit()
return {"ok": True}