From 6af3d3d2d04761fc9b4ac7d97e6f977933d6fdde Mon Sep 17 00:00:00 2001 From: huangzhuang_3rd Date: Wed, 12 Aug 2026 13:30:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20AI=20Worker=20=E5=B9=B3=E5=8F=B0=20MVP?= =?UTF-8?q?=20v1.0.0=20-=20=E5=A4=9A=E7=A7=9F=E6=88=B7/=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86/AI=20Worker/=E4=BB=BB=E5=8A=A1=E7=BC=96?= =?UTF-8?q?=E6=8E=92/HITL=E5=AE=A1=E6=A0=B8/=E6=88=90=E6=9C=AC=E6=B2=BB?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/core/tenant.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 backend/app/core/tenant.py diff --git a/backend/app/core/tenant.py b/backend/app/core/tenant.py new file mode 100644 index 0000000..036d953 --- /dev/null +++ b/backend/app/core/tenant.py @@ -0,0 +1,29 @@ +"""Tenant context: ensures all queries are tenant-scoped.""" +from typing import Optional +from fastapi import Request + + +class TenantContext: + """Thread-local-ish tenant context (stored on request state).""" + + @staticmethod + def get_tenant_id(request: Request) -> Optional[int]: + """Extract tenant_id from request state (set by middleware).""" + return getattr(request.state, "tenant_id", None) + + @staticmethod + def get_user_id(request: Request) -> Optional[int]: + """Extract user_id from request state.""" + return getattr(request.state, "user_id", None) + + @staticmethod + def get_user_role(request: Request) -> Optional[str]: + """Extract user role from request state.""" + return getattr(request.state, "user_role", None) + + +def apply_tenant_filter(query, model, tenant_id: int): + """Apply tenant_id filter to a SQLAlchemy query if the model has tenant_id.""" + if hasattr(model, "tenant_id"): + return query.filter(model.tenant_id == tenant_id) + return query