Files
xenia-pos-local/local_backend/routers/deps.py
bonamin 34ae328b0d feat: bump client-services (accumulated feature work + deploy fixes)
Snapshot of in-progress work across local_backend, manager_dashboard,
and waiter_pwa (pricing, chat, fiscal, prep zones, recovery codes, CRM,
inventory, permissions), plus the nginx/docker-compose deploy fixes for
the Unraid + NPM reverse-proxy setup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 10:00:14 +03:00

154 lines
5.3 KiB
Python

import jwt
from datetime import datetime, timedelta, timezone
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from database import get_db
from config import settings
from models.user import User
bearer = HTTPBearer()
# In-memory token blacklist (cleared on restart — acceptable for local use)
_blacklisted_tokens: set[str] = set()
TOKEN_EXPIRY_HOURS = 8
MASTER_USER_ID = 0 # sentinel — never a real DB row
def _make_ghost_superadmin():
"""Synthetic in-memory superadmin — plain namespace, never touches SQLAlchemy."""
from types import SimpleNamespace
return SimpleNamespace(
id=MASTER_USER_ID,
username=settings.MASTER_USERNAME,
full_name="Xenia Support",
role="superadmin",
is_active=True,
pin_hash=None,
password_hash=None,
email=None,
nickname=None,
mobile_phone=None,
note=None,
avatar_url=None,
waiter_settings=None,
hourly_rate=None,
created_at=datetime(2024, 1, 1, tzinfo=timezone.utc),
zone_assignments=[],
perm_access_dashboard=True,
perm_access_waiter_app=True,
perm_access_kds=True,
perm_cancel_orders=True,
perm_apply_discounts=True,
perm_modify_prices=True,
perm_open_orders=True,
perm_close_orders=True,
perm_view_reports=True,
perm_manage_staff=True,
perm_manage_tables=True,
perm_manage_menu=True,
perm_manage_settings=True,
)
def make_token(user: User) -> str:
payload = {
"sub": str(user.id),
"username": user.username,
"role": user.role,
"exp": datetime.now(timezone.utc) + timedelta(hours=TOKEN_EXPIRY_HOURS),
}
return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
def decode_token(token: str) -> dict:
if token in _blacklisted_tokens:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token revoked")
try:
return jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
def blacklist_token(token: str):
_blacklisted_tokens.add(token)
def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(bearer),
db: Session = Depends(get_db),
) -> User:
payload = decode_token(credentials.credentials)
if int(payload["sub"]) == MASTER_USER_ID and payload.get("role") == "superadmin":
return _make_ghost_superadmin()
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")
return user
def _has_perm(user: User, perm: str) -> bool:
"""Superadmins always have every permission."""
if user.role == "superadmin":
return True
return bool(getattr(user, perm, False))
def require_manager(user: User = Depends(get_current_user)) -> User:
"""Allow anyone with dashboard access (formerly role-based manager check)."""
if not _has_perm(user, "perm_access_dashboard"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Dashboard access required")
return user
def require_staff_manager(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_manage_staff"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Staff management permission required")
return user
def require_reports(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_view_reports"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Reports permission required")
return user
def require_menu_manager(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_manage_menu"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Menu management permission required")
return user
def require_settings_manager(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_manage_settings"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Settings management permission required")
return user
def require_kds(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_access_kds"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Δεν έχετε πρόσβαση στο KDS")
return user
def require_tables_manager(user: User = Depends(get_current_user)) -> User:
if not _has_perm(user, "perm_manage_tables"):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Tables management permission required")
return user
def require_superadmin(user: User = Depends(get_current_user)) -> User:
if user.role != "superadmin":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Superadmin access required")
return user
# Kept for any callers that still use the old name
require_sysadmin = require_superadmin