// ============================================================================= // NetForge Prisma Schema // ============================================================================= // // MULTI-TENANCY & RLS CONVENTION: // All tenant-scoped models MUST include a `tenantId` field. // This field is the foundation for Row-Level Security (RLS) policies. // When adding new models (Subscriber, Invoice, Plan, Payment, etc.), // always include: tenantId String + @@index([tenantId]) // // Super-admin models that span tenants (e.g., audit logs, platform config) // are the only exception to this rule. // ============================================================================= generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ============================================================================= // ENUMS // ============================================================================= enum TenantStatus { ACTIVE PENDING_SUSPENSION SUSPENDED } enum Role { ADMIN OFFICE_STAFF COLLECTOR TECHNICIAN CLIENT } // ============================================================================= // MODELS // ============================================================================= /// A Tenant represents a single ISP business using the NetForge platform. /// All tenant-scoped data is isolated by tenantId (RLS-ready). model Tenant { id String @id @default(uuid()) name String /// URL-friendly identifier, auto-generated from name (e.g., "my-isp" from "My ISP") slug String @unique ownerEmail String /// Physical address of the ISP business (optional) businessAddress String? /// Primary contact phone number (optional) contactPhone String? status TenantStatus @default(ACTIVE) /// Timestamp when suspension was triggered (starts grace period clock) suspendedAt DateTime? /// When actual service interruption occurs (suspendedAt + 7 days grace period) gracePeriodEndsAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt users User[] } /// A User belongs to a Tenant (or is a super-admin with no tenant). /// Email uniqueness is enforced per-tenant, not globally. /// Super-admins have isSuperAdmin=true and tenantId=null. model User { id String @id @default(uuid()) email String passwordHash String firstName String lastName String /// Nullable for super-admins who are not scoped to a specific tenant tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) /// Multi-role support — a user can hold more than one role within a tenant roles Role[] isActive Boolean @default(true) isSuperAdmin Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// Email must be unique within a tenant (super-admins have tenantId=null) @@unique([email, tenantId]) /// RLS-ready index — always present on tenant-scoped models @@index([tenantId]) }