from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import text from .auth import hash_password, verify_password, require_token from .db import get_db # Staff sign-in: email + password → the staff member's bearer token (the SPA stores it as before, # so nothing downstream changes — this just puts a friendly credential in front of the token). router = APIRouter(prefix="/auth", tags=["auth"]) class LoginIn(BaseModel): email: str password: str @router.post("/login") async def login(body: LoginIn, db=Depends(get_db)): row = (await db.execute(text( "SELECT id, name, role, token, password_hash FROM staff WHERE lower(email)=lower(:e) AND active"), {"e": body.email.strip()})).mappings().first() if not row or not verify_password(body.password, row["password_hash"]): raise HTTPException(401, "wrong email or password") await db.execute(text("UPDATE staff SET last_seen=now() WHERE id=:i"), {"i": row["id"]}) await db.commit() return {"ok": True, "token": row["token"], "name": row["name"], "role": row["role"]} class ChangePwIn(BaseModel): current_password: str | None = None new_password: str @router.post("/change-password") async def change_password(body: ChangePwIn, ident=Depends(require_token), db=Depends(get_db)): sid = ident.get("staff_id") if not sid: raise HTTPException(400, "the owner signs in with the master token; only staff have passwords") if len(body.new_password) < 6: raise HTTPException(400, "password too short (min 6 characters)") cur = (await db.execute(text("SELECT password_hash FROM staff WHERE id=:i"), {"i": sid})).scalar() # if a password is already set, the old one must check out; first-time set needs no current pw if cur and not verify_password(body.current_password or "", cur): raise HTTPException(401, "current password is incorrect") await db.execute(text("UPDATE staff SET password_hash=:h WHERE id=:i"), {"h": hash_password(body.new_password), "i": sid}) await db.commit() return {"ok": True}