معماری
چندفضایکاری
فضاهای کاری مهمترین و تغییرناپذیرترین اصل در کدبیس است. یک باگ که دادهها را در مرزهای فضای کاری نشت دهد، یک حادثه امنیتی محسوب میشود. اعمال این اصل در لایههای مختلف انجام میشود: ویژگیها، محدودههای سراسری، سیاستها و یک تست بازگشتی که در صورت شکست، فرآیند ساخت را متوقف میکند.
الگوی ویژگیها
دو ویژگی کار اصلی را انجام میدهند:
App\Concerns\BelongsToWorkspace— برای مدلهایی که مستقیماً ستونworkspace_idدارند.App\Concerns\BelongsToAgent— برای مدلهایی که به یک دستیار فروش تعلق دارند (و بهطور غیرمستقیم به فضای کاری آن دستیار فروش). محدوده سراسری این ویژگی از طریقagentsبهagents.workspace_id = currentمتصل میشود تا فیلتر کند.
هر دو ویژگی یک محدوده سراسری اضافه میکنند که به هر پرسوجوی Eloquent در برابر مدل اعمال میشود. آنها همچنین در زمان ایجاد، workspace_id را بهطور خودکار پر میکنند، بنابراین نمیتوانید بهطور تصادفی یک ردیف را در فضای کاری اشتباه درج کنید.
فضای کاری فعلی
فضای کاری فعلی در هر درخواست توسط میانافزار حل میشود:
- درخواستهای احراز هویتشده —
users.default_workspace_idرا میخواند، عضویت را تأیید کرده و نمونه singleton را تنظیم میکند. - درخواستهای ویجت — از ادعای
agent_idدر JWT استخراج میکند، دستیار فروش را پیدا کرده و singleton را ازagents.workspace_idتنظیم میکند. - وظایف سیستمی / صف — بهطور صریح به ازای هر وظیفه تنظیم میشوند، هرگز به ارث برده نمیشوند.
حلکننده در App\Support\CurrentWorkspace قرار دارد. پس از درخواست، بلوک finally میانافزار آن را پاک میکند تا وضعیت بین درخواستهای Octane نشت نکند.
دور زدن محدوده (نادر)
برخی پرسوجوها واقعاً نیاز به بررسی در بین فضاهای کاری دارند — گزارشهای مدیریت، ورود به حساب کاربری، وظایف سیستمی. دور زدن بهصورت صریح انجام میشود:
// عملیات سطح سیستم: جمعآوری مصرف در تمام فضاهای کاری
Workspace::withoutWorkspaceScope()->each(...)
قرارداد این است که هر فراخوانی withoutWorkspaceScope() باید یک کامنت توجیهی بلافاصله بالای خود داشته باشد. بازبینی کد این را اعمال میکند؛ دستور اسلش حسابرسی /tenancy تخلفات را علامتگذاری میکند.
تغییر فضای کاری
یک کاربر با چندین عضویت از انتخابگر فضای کاری در نوار کناری استفاده میکند. تغییر، یک درخواست POST به /workspaces/{id}/select ارسال میکند که users.default_workspace_id را بهروز کرده و هدایت میکند. بارگذاری بعدی صفحه، فضای کاری جدید را حل میکند.
انتخابگر فقط زمانی نمایش داده میشود که کاربر ۲+ عضویت داشته باشد — کاربران تکفضایکاری بهجای منو، یک برچسب آرام میبینند، زیرا "تغییر" بین یک گزینه فقط شلوغی است.
دادههای بهازای هر فضای کاری
جدولهای محدود به فضای کاری (هر مدل Eloquent با ویژگی):
- مستقیم
workspace_id:agents،integration_connections،plan_subscriptions،usage_events،audit_logs،invitations. - از طریق دستیار فروش:
agent_versions،behavior_rules،cta_rules،curated_answers،experiments،sources،documents،chunks،visitors،conversations،messages،leads،content_gaps.
جدولهای بینفضایکاری (بدون محدوده):
users— سراسری. عضویت در هر فضای کاری در جدول اتصال است.workspaces— سراسری. خود فضای کاری به یک فضای کاری محدود نمیشود.plans— سراسری، مدیریتشده توسط مدیران پلتفرم.jobs/failed_jobs/notifications— زیرساخت لاراول.app_settings— singleton، سراسری پلتفرم.
ایزولهسازی ذخیرهساز جستجوی هوشمند
فراداده ذخیرهساز جستجوی هوشمند، قرارداد فضاهای کاری را منعکس میکند — هر نقطه دارای برچسبهای agent_id و workspace_id است و هر پرسوجو با agent_id فیلتر میشود. Cloudflare Vectorize و Qdrant هر دو این را بهصورت بومی پشتیبانی میکنند (فیلتر payload Qdrant / فیلتر فراداده Vectorize).
یک باگ که فقط با agent_id فیلتر میکرد اما فضای کاری واقعی دستیار فروش را نه، همچنان ایمن بود — شناسههای دستیار فروش ULID هستند و بهطور جهانی منحصربهفردند. یک باگ که اصلاً فیلتر نمیکرد یک نشت محسوب میشد. Retriever همیشه بهطور صریح فیلتر میکند.
مجوزها (سیاستها)
فضاهای کاری به این معنی است: "آیا این ردیف در فضای کاری من است؟". مجوز به این معنی است: "با ردیفهای موجود در فضای کاری من چه کاری میتوانم انجام دهم؟". سیاستها در app/Policies/ قرار دارند:
WorkspacePolicy— مشاهده/بهروزرسانی/حذف خود فضای کاری، انتقال مالکیت.AgentPolicy— viewAny / view / create / update / delete / publish / rollback.SourcePolicy— مدیریت منابع در یک دستیار فروش.LeadPolicy— خواندن / بهروزرسانی / حذف مشتریان بالقوه.IntegrationConnectionPolicy— اتصال / قطع اتصال.
الگوی بررسی ثابت است: $user->can('update', $agent) یا abort_if(! $user->can(...)). سیاستها از راهنمای Tenancy برای حل نقش کاربر در فضای کاری منبع استفاده کرده و سپس متدهای قابلیت را روی enum WorkspaceRole فراخوانی میکنند.
تستها
تست بازگشتی MultiTenancyTest در tests/Feature/ قرار دارد. دو فضای کاری با دادههای همپوشانی ایجاد کرده و تأیید میکند که پرسوجوها از هر کدام فقط میتوانند ردیفهای خود را ببینند. همچنین هر مدل در app/Models/ را بررسی میکند تا تأیید کند هر مدلی با workspace_id از ویژگی استفاده میکند.
اجرا:
php artisan test --filter=MultiTenancyTest
لازم است در هر ساخت CI قبول شود. درخواستهای کششی که آن را خراب میکنند، نمیتوانند ادغام شوند.