156 lines
4.2 KiB
Python
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}
|