# -*- coding: utf-8 -*- """园区租户认证 —— 大屏登录(账号密码 → 长效 tenant token,一次登录持久保持)。""" from __future__ import annotations import datetime import jwt from fastapi import Header, HTTPException from .config import settings _SECRET = settings.ADMIN_SECRET _ALG = "HS256" _TTL_DAYS = 365 def create_token(tenant_id: str) -> str: now = datetime.datetime.now(datetime.timezone.utc) payload = { "typ": "park", "tid": tenant_id, "iat": now, "exp": now + datetime.timedelta(days=_TTL_DAYS), } return jwt.encode(payload, _SECRET, algorithm=_ALG) def parse_token(token: str) -> str: """返回 tenant_id;非法/过期抛 401。""" try: payload = jwt.decode(token, _SECRET, algorithms=[_ALG]) except jwt.PyJWTError as e: # noqa: BLE001 raise HTTPException(status_code=401, detail=f"park token 无效: {e}") from e if payload.get("typ") != "park" or not payload.get("tid"): raise HTTPException(status_code=401, detail="park token 无效") return payload["tid"] def require_tenant(authorization: str | None = Header(default=None, description="Bearer ")) -> str: """大屏接口鉴权:从 Authorization 解析 tenant_id。""" if not authorization or not authorization.startswith("Bearer "): raise HTTPException(status_code=401, detail="缺少 park token") return parse_token(authorization.split(" ", 1)[1])