92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
import bcrypt
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from database import get_db
|
|
from models.user import User
|
|
from schemas.auth import LoginRequest, TokenResponse
|
|
from pydantic import BaseModel as _PydanticBase
|
|
|
|
class LoginByIdRequest(_PydanticBase):
|
|
waiter_id: int
|
|
pin: str
|
|
from schemas.user import UserOut
|
|
from routers.deps import get_current_user, make_token, decode_token, blacklist_token
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/login", response_model=TokenResponse)
|
|
def login(body: LoginRequest, db: Session = Depends(get_db)):
|
|
user = db.query(User).filter(User.username == body.username, User.is_active == True).first()
|
|
if not user or not bcrypt.checkpw(body.pin.encode(), user.pin_hash.encode()):
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
|
token = make_token(user)
|
|
return TokenResponse(access_token=token, user=UserOut.model_validate(user))
|
|
|
|
|
|
@router.post("/login-by-id", response_model=TokenResponse)
|
|
def login_by_id(body: LoginByIdRequest, db: Session = Depends(get_db)):
|
|
"""Login using waiter id + PIN (used by the waiter-picker login screen)."""
|
|
user = db.query(User).filter(User.id == body.waiter_id, User.is_active == True).first()
|
|
if not user or not bcrypt.checkpw(body.pin.encode(), user.pin_hash.encode()):
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Λανθασμένο PIN")
|
|
token = make_token(user)
|
|
return TokenResponse(access_token=token, user=UserOut.model_validate(user))
|
|
|
|
|
|
@router.post("/refresh", response_model=TokenResponse)
|
|
def refresh(token: str, db: Session = Depends(get_db)):
|
|
payload = decode_token(token)
|
|
user = db.query(User).filter(User.id == int(payload["sub"]), User.is_active == True).first()
|
|
if not user:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
|
|
blacklist_token(token)
|
|
new_token = make_token(user)
|
|
return TokenResponse(access_token=new_token, user=UserOut.model_validate(user))
|
|
|
|
|
|
@router.post("/logout")
|
|
def logout(token: str):
|
|
blacklist_token(token)
|
|
return {"status": "logged out"}
|
|
|
|
|
|
@router.get("/me", response_model=UserOut)
|
|
def me(user: User = Depends(get_current_user)):
|
|
return user
|
|
|
|
|
|
# ─── Public waiter list (login screen — no auth required) ────────────────────
|
|
|
|
from pydantic import BaseModel as _BaseModel
|
|
|
|
class PublicWaiterOut(_BaseModel):
|
|
id: int
|
|
full_name: str | None
|
|
nickname: str | None
|
|
avatar_url: str | None
|
|
on_shift: bool
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
@router.get("/waiters", response_model=list[PublicWaiterOut])
|
|
def public_waiter_list(db: Session = Depends(get_db)):
|
|
"""Public endpoint — returns active waiters with on-shift flag. No auth required."""
|
|
from models.shift import WaiterShift
|
|
waiters = db.query(User).filter(User.role == "waiter", User.is_active == True).all()
|
|
on_shift_ids = {
|
|
row.waiter_id
|
|
for row in db.query(WaiterShift).filter(WaiterShift.ended_at == None).all()
|
|
}
|
|
return [
|
|
PublicWaiterOut(
|
|
id=w.id,
|
|
full_name=w.full_name,
|
|
nickname=w.nickname,
|
|
avatar_url=w.avatar_url,
|
|
on_shift=w.id in on_shift_ids,
|
|
)
|
|
for w in waiters
|
|
]
|