# context.py
from contextvars import ContextVar
from typing import Optional
from pydantic import BaseModel
from fastapi import Request
class Context(BaseModel):
"""
我们的'当前'上下文 - 类似于Ruby的Current模式。
包含已认证的用户和常用的引用。
"""
issuer_id: str
issuer_role: UserRole
issuer_email: str
issuer_status: UserStatus
organization_id: Optional[str] = None
organization_name: Optional[str] = None
organization_member_role: Optional[OrganizationMemberRole] = None
organization_member_status: Optional[OrganizationMemberStatus] = None
feature_flags: dict[ConfigKey, FeatureFlagConfig] = {}
config: UserConfig
client_info: dict = {}
def is_admin(self) -> bool:
return self.issuer_role == UserRole.ADMIN
def has_feature_flag(self, key: ConfigKey) -> bool:
return self.config.check_ff(key)
def verify_is_admin(self) -> None:
if self.issuer_role != UserRole.ADMIN:
raise ForbiddenError(
type=ErrorType.UNAUTHORIZED,
description='您无权访问此资源',
)
@classmethod
def for_user(cls, user: UserSchema) -> Self:
organization_id = user.organization.id if user.organization else None
organization_member_role = user.organization.role if user.organization else None
return cls(
issuer_id=user.id,
issuer_role=user.role,
issuer_status=user.status,
issuer_email=user.email,
organization_id=organization_id,
organization_name=user.organization.name if user.organization else None,
organization_member_role=organization_member_role,
config=UserConfig(),
)
class _ContextAttributes:
_context: ContextVar[Optional[Context]] = ContextVar('current_context', default=None)
@property
def context(self) -> Context:
ctx = self._context.get()
if ctx is None:
raise RuntimeError('没有可用的上下文 - 不在请求范围内')
return ctx
@property
def user_id(self) -> str:
return self.context.issuer_id
def set(self, context: Context):
self._context.set(context)
def clear(self):
self._context.set(None)
# 全局Current实例
Current = _ContextAttributes()