mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-27 03:56:33 +00:00
- Delete sqlite-adapter.ts and migrations.ts (SQLite path removed) - Remove driver/sqlitePath from all config schemas + sec-config template - init.ts now PG-only, no SQLite branch - db-adapter.ts dialect narrowed to "postgres" only - Add in-place UUIDv7 migration: detects INTEGER PKs in existing PG databases, drops FK constraints, ALTER COLUMN TYPE to TEXT for all 15 entity tables + their FK columns, re-adds FK constraints. Idempotent (skips if already TEXT). Existing integer IDs become string "1", "2" etc — new inserts use proper UUIDv7 from repository.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
/**
|
|
* Multi-tenant schema-per-tenant model.
|
|
*
|
|
* PostgreSQL database layout:
|
|
* public schema:
|
|
* tenants — registry of all tenants
|
|
* global_admins — platform-level admins who can provision tenants
|
|
*
|
|
* tenant_<uuid> schema (one per tenant):
|
|
* full BetterFrame table set (users, cameras, kiosks, layouts, etc.)
|
|
*
|
|
* Request flow:
|
|
* 1. Session / API key / kiosk key → resolve tenant_id
|
|
* 2. SET search_path = tenant_<id>, public
|
|
* 3. All queries run against tenant's schema
|
|
* 4. Connection returned to pool with search_path reset
|
|
*
|
|
* Default tenant uses the public schema directly (slug = "default").
|
|
*/
|
|
|
|
export const DEFAULT_TENANT_ID = "default";
|
|
|
|
export interface Tenant {
|
|
id: string; // UUID
|
|
name: string;
|
|
slug: string; // URL-safe, unique
|
|
schema_name: string; // "tenant_<uuid>" — Postgres schema
|
|
is_active: boolean;
|
|
max_kiosks: number | null; // null = unlimited
|
|
max_cameras: number | null;
|
|
max_users: number | null;
|
|
created_at: string;
|
|
}
|
|
|
|
/**
|
|
* Derive the Postgres schema name from a tenant ID.
|
|
* Sanitized to prevent SQL injection — only alphanumeric + underscore.
|
|
*/
|
|
export function tenantSchemaName(tenantId: string): string {
|
|
const safe = tenantId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 63);
|
|
return `tenant_${safe}`;
|
|
}
|
|
|
|
/**
|
|
* SQL to set the search path for a tenant's schema.
|
|
* Always includes `public` so shared tables (like a future
|
|
* tenant registry) are accessible.
|
|
*/
|
|
export function setTenantSearchPath(tenantId: string): string {
|
|
const schema = tenantSchemaName(tenantId);
|
|
return `SET search_path = "${schema}", public`;
|
|
}
|