Files
afilmory/apps/docs/contents/saas/architecture.mdx
Innei 019ee50121 feat: implement OAuth state management for multi-tenancy support
- Introduced encoding and decoding of OAuth state to include tenant metadata, allowing the gateway to route callbacks without hard-coded tenant slugs.
- Updated the AuthController to handle social account linking and sign-in with compatibility for legacy paths.
- Refactored redirect URI construction to simplify tenant slug handling.
- Enhanced documentation to reflect changes in the OAuth flow and state management.

Signed-off-by: Innei <tukon479@gmail.com>
2025-11-30 14:03:05 +08:00

56 lines
2.5 KiB
Plaintext

---
title: SaaS Architecture
description: Tenant model, domain routing, OAuth flow, and data injection paths.
createdAt: 2025-11-23T20:20:00+08:00
lastModified: 2025-11-30T14:03:05+08:00
order: 36
---
# SaaS Architecture
## Tenant model
- Tenants identified by **slug**; reserved: `root` (superadmin) and the placeholder slug.
- Status checks: active, suspended, banned (requests are rejected with dedicated error codes).
- Per-tenant plans and quotas enforced by `BillingPlanService` + `BillingUsageService`.
## Domains
- **Base domain** (default `afilmory.art`, editable in System Settings): tenants available at `<slug>.<baseDomain>`.
- **Custom domains**: tenant can request a domain; verification passes if CNAME points to `<baseDomain>` or TXT contains the issued token (handled by `TenantDomainService`).
- **Placeholder tenant**: used when slug is missing to render onboarding instead of erroring.
## Tenant resolution (core)
1. Existing context? reuse.
2. Match custom domain → `TenantDomainService`.
3. Else derive slug from host via `extractTenantSlugFromHost` and `baseDomain`.
4. Root/admin paths fallback to `root` tenant.
5. If still none, apply placeholder (unless configured to throw).
## OAuth gateway flow
- Providers redirect to the fixed `https://auth.<baseDomain>/api/auth/callback/{provider}`.
- Tenant slug is wrapped into the OAuth `state` so the gateway can restore the inner Better Auth state and forward to `https://<slug>.<baseDomain>/api/auth/callback/{provider}`.
- Keeps provider config simple (single callback URL) while maintaining per-tenant sessions.
## Data path
- Core serves the SPA and dashboard static bundles.
- Before sending `index.html`, it injects `window.__CONFIG__` and `window.__MANIFEST__` built from live DB/storage, so the frontend stays source-agnostic.
## Static assets roots
- Web: `apps/web` build copied into `be/apps/core/dist/static/web`.
- Dashboard: `be/apps/dashboard` build copied into `be/apps/core/dist/static/dashboard`.
- During tenant resolution, static roots are chosen per route segment; fallback logs are emitted if missing.
## Billing & plans
- System Settings define plan catalogs/pricing/overrides (billing + storage).
- Usage is aggregated per tenant; superadmin can change plan or ban via `/api/super-admin/tenants`.
## Root account provisioning
- On first boot, `RootAccountProvisioner` creates/updates the superadmin (`DEFAULT_SUPERADMIN_EMAIL/USERNAME`) and prints credentials and `/platform/root-login` URLs to stdout.