Multi-tenant архітектура
Translator note: machine-translated baseline. Needs human review by a native UK speaker. EN source is canonical.
casinocrm.io є multi-tenant на рівні бази даних через Postgres Row-Level Security (RLS). Кожна tenant-scoped таблиця має RLS policy, яка фільтрує по current_setting('app.tenant_id') — встановлено per-request з автентифікованої session. Cross-tenant data leakage неможливе без явного SET ROLE на привілейовану роль (використовується тільки webhook routing helpers + admin platform tools).
Що є tenant-isolated
Кожна domain таблиця:
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 таблиці (без RLS):
tenants(SSOT для tenant rows)users+tenant_memberships(auth wiring)webhook_events_processed(idempotency ledger — pre-auth dedupe)__ucrm_migrations(schema version)
Як requests scoped
- Admin user автентифікується через Clerk → middleware resolve-ить
(user_id, tenant_id)зtenant_membershipsrow. - Request handler обгортає DB доступ у
withTenant(tenantId, async (tx) => …), якийBEGIN+SET LOCAL ROLE ucrm_app+SET LOCAL app.tenant_id = $tenant. - Кожен query всередині closure RLS-фільтрується автоматично.
Д ля server-to-server endpoints (/v1/internal/*) caller передає tenant_id у body + handler використовує withTenant(body.tenant_id, …). Internal-secret middleware автентифікує, що caller — легітимний casinocrm.io сервіс.
Чому це важливо
- Жодної випадкової cross-tenant exposure — навіть SQL injection, що обійшов би Drizzle, не злив дані поза tenant context.
- Per-tenant disaster recovery — drop єдиного tenant через
DELETE FROM tenants WHERE id = ?каскадує через кожну domain таблицю. - Self-host friendly — оператори можуть запускати casinocrm.io з одним tenant + все ще отримувати користь від isolation pattern (futureproofing для resellers / white-label).