Spaces:
Sleeping
Sleeping
| """Token QR bàn — ký `itsdangerous`, payload (tenant_id, table_id). | |
| Token KHÔNG có hạn dùng, cố ý: QR in ra dán lên bàn là vật lý vĩnh viễn, | |
| "hết hạn" thuộc về guest_session (TTL 24h, bảng `guest_sessions`), không | |
| thuộc về token. Thu hồi QR = đổi POOLCOACH_SECRET (mọi token cũ chết cùng lúc). | |
| Secret từ env `POOLCOACH_SECRET`; không set → default dev + warning lúc boot | |
| (main.py in). Default dev để chạy local không cần cấu hình gì — nhưng token | |
| ký bằng nó KHÔNG có giá trị bảo mật. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from itsdangerous import BadSignature, URLSafeSerializer | |
| DEV_SECRET = "poolcoach-dev-secret-khong-dung-cho-prod" | |
| _SALT = "poolcoach-table-qr" | |
| def secret_is_default() -> bool: | |
| return not os.environ.get("POOLCOACH_SECRET") | |
| def _serializer() -> URLSafeSerializer: | |
| # Đọc env MỖI LẦN thay vì cache lúc import: test đổi secret bằng | |
| # monkeypatch.setenv phải có tác dụng ngay. | |
| secret = os.environ.get("POOLCOACH_SECRET") or DEV_SECRET | |
| return URLSafeSerializer(secret, salt=_SALT) | |
| def make_table_token(tenant_id: int, table_id: int) -> str: | |
| return _serializer().dumps([tenant_id, table_id]) | |
| def read_table_token(token: str) -> tuple[int, int] | None: | |
| """Trả (tenant_id, table_id), hoặc None nếu chữ ký sai / payload lạ.""" | |
| try: | |
| data = _serializer().loads(token) | |
| except BadSignature: | |
| return None | |
| # Chữ ký đúng nhưng payload không đúng shape vẫn là token rác (ví dụ | |
| # token ký cho mục đích khác cùng secret) — từ chối nốt. | |
| if (not isinstance(data, (list, tuple)) or len(data) != 2 | |
| or not all(isinstance(v, int) for v in data)): | |
| return None | |
| return int(data[0]), int(data[1]) | |