Multi-tenant architektura
casinocrm.io je multi-tenant na databázové vrstvě přes Postgres Row-Level Security (RLS). Každá tenant-scoped tabulka má RLS policy, která filtruje přes current_setting('app.tenant_id') — nastavováno per-request z autentizované session. Cross-tenant data leakage je nemožný bez explicitního SET ROLE na privileged role (používáno pouze webhook routing helpery + admin platform tools).
Co je tenant-isolated
Každá doménová tabulka:
players+player_wallets+player_tier_history+kyc_historysegments+messaging_providers+message_templatescampaigns+campaign_variants+message_sendsjourneys+journey_runs+journey_node_runsbonus_templates+bonus_grants+wagering_events+bonus_campaignsvip_tierscurrencies+fx_ratesaudit_logsadapter_configs
Cross-tenant tabulky (bez RLS):
tenants(SSOT pro tenant rows)users+tenant_memberships(auth wiring)webhook_events_processed(idempotency ledger — pre-auth dedupe)__ucrm_migrations(schema version)
Jak jsou requesty scoped
- Admin user se autentizuje přes Clerk → middleware resolvne
(user_id, tenant_id)ztenant_membershipsřádku. - Request handler obalí DB přístup do
withTenant(tenantId, async (tx) => …), kterýBEGIN+SET LOCAL ROLE ucrm_app+SET LOCAL app.tenant_id = $tenant. - Každý query uvnitř closure je RLS-filtrován automaticky.
Pro server-to-server endpointy (/v1/internal/*) caller předá tenant_id v body + handler použije withTenant(body.tenant_id, …). Internal-secret middleware autentizuje, že caller je legitimní casinocrm.io služba.
Proč na tom záleží
- Žádná náhodná cross-tenant exposure — i SQL injection, který by obešel Drizzle, by neunikl data mimo tenant kontext.
- Per-tenant disaster recovery — drop jediného tenanta přes
DELETE FROM tenants WHERE id = ?se cascade-uje napříč každou doménovou tabulkou. - Self-host friendly — operátoři mohou jet casinocrm.io s jedním tenantem + stále těžit z isolation patternu (futureproofing pro resellery / white-label).