TorbatYar/docs/last_step.md
Mortezakoohjani 800b0ba2c5 Deploy TorbatYar for torbatyar.ir with nginx multi-tenant routing.
Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 21:43:33 +03:30

87 lines
6.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# آخرین گام انجام‌شده
## فاز ۴ — Tenant Onboarding و Workspace Activation
> در بریف پروژه این کار «Phase 3: operationalizing tenant onboarding and
> workspace activation» نامیده شده؛ در شماره‌گذاری داخلی مستندات، چون «فاز
> ۳» قبلاً به OTP Login + Tenant Management اختصاص داشت، این تحویل **فاز ۴**
> ثبت شده است (نگاه کنید به `progress.md`).
### مسئله
تا پیش از این فاز، سیستم فقط «هویت + لیست ادمین» بود: کاربر با OTP/SSO وارد
می‌شد و از پنل ادمین tenantها را می‌دید/می‌ساخت، اما هیچ جریان واقعی
self-service برای این‌که یک کاربر عادی workspace خودش را بسازد، مالک آن شود،
پلن/دامنه/برندینگ بگیرد و وارد یک داشبورد عملیاتی شود، وجود نداشت.
### راه‌حل
**Backend (`core-service`):**
- جدول جدید `tenant_memberships` (نقش `tenant_owner/tenant_admin/tenant_editor/tenant_viewer/platform_admin`،
وضعیت `active/invited/disabled`، `is_owner`).
- چرخهٔ عمر tenant: `draft → pending_activation → active → suspended/archived`.
- ستون‌های برندینگ/پروفایل روی `tenants` + `is_primary`/`verification_status`
روی `domains` + `current_tenant_id` روی `users`.
- Seed پلن‌های `FREE`/`STARTER` و اتصال خودکار اشتراک `FREE` هنگام ساخت tenant.
- سرویس‌های جدید: `UserService` (یکپارچه‌سازی resolve کاربر از JWT محلی یا
کیکلوک)، `MembershipService`، `OnboardingService`، `TenantContextService`.
- APIهای جدید: `GET /me`, `GET /me/tenants`, `POST /onboarding/tenant`,
`PATCH .../branding`, `PATCH .../domain`, `POST .../complete`,
`GET /tenant/current`, `POST /tenant/switch`.
- Migration `0005_tenant_onboarding` + تست‌های `test_onboarding.py` (۷ تست،
جریان کامل + حالات forbidden/duplicate/owner-required).
**Frontend (`frontend`):**
- `lib/api.ts` با `api.me` / `api.onboarding` / `api.tenantContext`.
- `hooks/useMe.ts` برای بارگذاری `/api/v1/me`.
- `app/onboarding/page.tsx` — ویزارد ۴ مرحله‌ای (کسب‌وکار → برندینگ → دامنه
→ بازبینی) با قابلیت ازسرگیری onboarding ناتمام.
- `app/dashboard/page.tsx` — داشبورد واقعی workspace + redirect خودکار به
`/onboarding` وقتی `onboarding_required=true`.
- `components/TenantSwitcher.tsx` — سوییچر ساده چند-workspace.
### محدودیت‌های عمدی این فاز (خارج از scope)
- بدون درگاه پرداخت — فقط ساختار provisioning پلن/اشتراک.
- بدون تأیید واقعی DNS برای دامنهٔ اختصاصی (فقط رکورد `pending`).
- مسیر قدیمی `POST /admin/tenants` (فاز ۱/۲) بازنویسی نشده و هنوز عضویت/پلن
خودکار نمی‌سازد — عمداً دست‌نخورده ماند تا فازهای قبلی بازسازی نشوند.
- JIT provisioning کامل کاربر Core از JWT کیکلوک پیاده نشده (کاربر SSO بدون
رکورد Core فعلاً `403 forbidden` می‌گیرد؛ نگاه کنید به بخش بعد).
---
## فاز پیشنهادی بعدی: White-label Runtime Rendering
### چرا این فاز و نه یک ماژول بیزینسی؟
در همین فاز، فیلدهای برندینگ واقعی (`primary_color`, `secondary_color`,
`logo_url`, `favicon_url`) روی هر tenant ذخیره و در onboarding از کاربر
گرفته می‌شوند — اما frontend فعلاً رنگ‌ها را فقط از یک فایل استاتیک
(`public/theme.config.json`) می‌خواند و **هیچ ارتباطی با tenant واقعی
resolveشده ندارد**. یعنی خروجی onboarding (برندینگ tenant) هنوز در UI
اعمال نمی‌شود. تکمیل این حلقه (ذخیره برند → نمایش برند) از نظر ارزش محصول
و آمادگی فنی، اولویت بالاتری نسبت به شروع اولین ماژول بیزینسی (Accounting/
CRM/...) دارد؛ چون:
1. زیرساخت لازم (فیلدهای دیتابیس، APIهای `TenantContextRead`، CSS
Variables موجود در frontend) از قبل آماده است — فقط باید به هم وصل شوند.
2. بدون این حلقه، ادعای «SaaS چندمستأجری white-label» ناقص می‌ماند، در حالی
که هر ماژول بیزینسی جدید (Accounting/CRM/...) به خودی خود به این زیرساخت
وابسته نیست.
3. حل تشخیص tenant از دامنه (subdomain/custom domain) که برای white-label
لازم است، پیش‌نیاز طبیعی معماری چندمستأجری برای هر ماژول آینده نیز هست.
### پیشنهاد Scope فاز بعدی
1. **Backend:** endpoint عمومی (بدون نیاز به auth کاربر) برای resolve
theme بر اساس Host/X-Tenant-Slug، مثلاً
`GET /api/v1/public/tenant-theme` که `primary_color`, `secondary_color`,
`logo_url`, `favicon_url`, `business_name` را برمی‌گرداند (از همان
middleware تشخیص tenant موجود استفاده کند).
2. **Frontend:** بارگذاری theme از این endpoint در `ThemeProvider` هنگام
دسترسی از subdomain/custom domain یک tenant (به‌جای/علاوه‌بر
`theme.config.json` استاتیک فعلی که برای برند اصلی پلتفرم باقی می‌ماند).
3. مسیر عمومی مشتری/ویزیتور tenant (بدون نیاز به لاگین) که برند tenant را
می‌بیند — زیرساخت لازم برای هر ماژول بیزینسی آینده (مثلاً منوی دیجیتال
رستوران) که باید در دامنهٔ tenant با ظاهر آن tenant نمایش داده شود.
4. بعد از این فاز، اولین ماژول بیزینسی واقعی (پیشنهاد: «رستوران / منوی
دیجیتال» چون در `docs/architecture.md` به‌عنوان سرویس فعلی نام‌برده شده و
دیتابیس مفهومی آن (`restaurant_db`) از قبل طراحی شده) روی همین زیرساخت
white-label ساخته شود.