from functools import lru_cache from fastapi import Depends, Request from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from app.core.config import get_settings from app.core.contracts import RequestContext from app.core.errors import UnauthorizedAgentError from app.core.security import JwtAuthenticator from app.service.identity_service import IdentityService from app.service.risk_questionnaire_service import RiskQuestionnaireService _bearer = HTTPBearer(auto_error=False) TRACE_ID_HEADER = "X-Trace-ID" @lru_cache(maxsize=1) def _authenticator() -> JwtAuthenticator: return JwtAuthenticator(get_settings()) async def build_request_context( request: Request, credentials: HTTPAuthorizationCredentials | None = Depends(_bearer), # noqa: B008 ) -> RequestContext: request.state.trace_id = request.headers.get(TRACE_ID_HEADER) if credentials is None or credentials.scheme.lower() != "bearer": raise UnauthorizedAgentError("token is missing, invalid, or revoked") try: context = _authenticator().authenticate(credentials.credentials) context = await IdentityService().resolve(context) except Exception as exc: raise UnauthorizedAgentError("token is missing, invalid, or revoked") from exc request.state.request_context = context onboarding_path = request.url.path.startswith("/api/v1/onboarding/") if not onboarding_path and await RiskQuestionnaireService().is_required(context): from app.core.errors import OnboardingRequiredError raise OnboardingRequiredError("请先完成开户风险测评问卷") return context