/** * Seed script for Port Nimara CRM. * * Top-level orchestrator: * 1. Create 3 ports (idempotent): * - Port Nimara * - Marina Azzurra * - Harbor Royale * 2. Create 5 system roles with full permission maps * 3. Create the super admin user profile placeholder (matt@portnimara.com) * 4. For each port, call `seedPortData(portId, portSlug)` from seed-data.ts * to produce the realistic multi-cardinality fixture * (berths, clients, companies, yachts, memberships, interests, * reservations, ownership-transfer history). * 5. Print a summary. * * Run with: pnpm db:seed */ import 'dotenv/config'; import { eq } from 'drizzle-orm'; import { db } from './index'; import { ports } from './schema/ports'; import { roles, userProfiles } from './schema/users'; import type { RolePermissions } from './schema/users'; import { seedPortData, type SeedSummary } from './seed-data'; // ─── Permission Maps ───────────────────────────────────────────────────────── const ALL_PERMISSIONS: RolePermissions = { clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true }, interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true, }, berths: { view: true, edit: true, import: true, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true, }, expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true, }, invoices: { view: true, create: true, edit: true, delete: true, send: true, record_payment: true, export: true, }, files: { view: true, upload: true, delete: true, manage_folders: true }, email: { view: true, send: true, configure_account: true }, reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true, }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: true }, yachts: { view: true, create: true, edit: true, delete: true, transfer: true }, companies: { view: true, create: true, edit: true, delete: true }, memberships: { view: true, manage: true }, reservations: { view: true, create: true, activate: true, cancel: true }, admin: { manage_users: true, view_audit_log: true, manage_settings: true, manage_webhooks: true, manage_reports: true, manage_custom_fields: true, manage_forms: true, manage_tags: true, system_backup: true, }, residential_clients: { view: true, create: true, edit: true, delete: true }, residential_interests: { view: true, create: true, edit: true, delete: true, change_stage: true, }, }; const DIRECTOR_PERMISSIONS: RolePermissions = { clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true }, interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true, }, berths: { view: true, edit: true, import: true, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true, }, expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true, }, invoices: { view: true, create: true, edit: true, delete: true, send: true, record_payment: true, export: true, }, files: { view: true, upload: true, delete: true, manage_folders: true }, email: { view: true, send: true, configure_account: true }, reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true, }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: true }, yachts: { view: true, create: true, edit: true, delete: true, transfer: true }, companies: { view: true, create: true, edit: true, delete: true }, memberships: { view: true, manage: true }, reservations: { view: true, create: true, activate: true, cancel: true }, admin: { manage_users: true, view_audit_log: true, manage_settings: true, manage_webhooks: true, manage_reports: true, manage_custom_fields: true, manage_forms: true, manage_tags: true, system_backup: false, }, residential_clients: { view: true, create: true, edit: true, delete: true }, residential_interests: { view: true, create: true, edit: true, delete: true, change_stage: true, }, }; const SALES_MANAGER_PERMISSIONS: RolePermissions = { clients: { view: true, create: true, edit: true, delete: false, merge: true, export: true }, interests: { view: true, create: true, edit: true, delete: false, change_stage: true, generate_eoi: true, export: true, }, berths: { view: true, edit: true, import: false, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: false, }, expenses: { view: true, create: true, edit: true, delete: false, export: true, scan_receipt: true, }, invoices: { view: true, create: true, edit: true, delete: false, send: true, record_payment: true, export: true, }, files: { view: true, upload: true, delete: false, manage_folders: true }, email: { view: true, send: true, configure_account: true }, reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true, }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: false }, yachts: { view: true, create: true, edit: true, delete: false, transfer: true }, companies: { view: true, create: true, edit: true, delete: false }, memberships: { view: true, manage: true }, reservations: { view: true, create: true, activate: true, cancel: true }, admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: true, system_backup: false, }, residential_clients: { view: false, create: false, edit: false, delete: false }, residential_interests: { view: false, create: false, edit: false, delete: false, change_stage: false, }, }; const SALES_AGENT_PERMISSIONS: RolePermissions = { clients: { view: true, create: true, edit: true, delete: false, merge: false, export: true }, interests: { view: true, create: true, edit: true, delete: false, change_stage: true, generate_eoi: true, export: true, }, berths: { view: true, edit: true, import: false, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: false, }, expenses: { view: true, create: true, edit: true, delete: false, export: true, scan_receipt: true, }, invoices: { view: true, create: true, edit: true, delete: false, send: true, record_payment: true, export: true, }, files: { view: true, upload: true, delete: false, manage_folders: false }, email: { view: true, send: true, configure_account: true }, reminders: { view_own: true, view_all: false, create: true, edit_own: true, edit_all: false, assign_others: false, }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: false }, yachts: { view: true, create: true, edit: true, delete: false, transfer: false }, companies: { view: true, create: true, edit: false, delete: false }, memberships: { view: true, manage: false }, reservations: { view: true, create: true, activate: true, cancel: false }, admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: true, system_backup: false, }, residential_clients: { view: false, create: false, edit: false, delete: false }, residential_interests: { view: false, create: false, edit: false, delete: false, change_stage: false, }, }; const VIEWER_PERMISSIONS: RolePermissions = { clients: { view: true, create: false, edit: false, delete: false, merge: false, export: false }, interests: { view: true, create: false, edit: false, delete: false, change_stage: false, generate_eoi: false, export: false, }, berths: { view: true, edit: false, import: false, manage_waiting_list: false }, documents: { view: true, create: false, send_for_signing: false, upload_signed: false, delete: false, }, expenses: { view: true, create: false, edit: false, delete: false, export: false, scan_receipt: false, }, invoices: { view: true, create: false, edit: false, delete: false, send: false, record_payment: false, export: false, }, files: { view: true, upload: false, delete: false, manage_folders: false }, email: { view: true, send: false, configure_account: false }, reminders: { view_own: true, view_all: false, create: false, edit_own: false, edit_all: false, assign_others: false, }, calendar: { connect: false, view_events: true }, reports: { view_dashboard: true, view_analytics: false, export: false }, document_templates: { view: true, generate: false, manage: false }, yachts: { view: true, create: false, edit: false, delete: false, transfer: false }, companies: { view: true, create: false, edit: false, delete: false }, memberships: { view: true, manage: false }, reservations: { view: true, create: false, activate: false, cancel: false }, admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: false, system_backup: false, }, residential_clients: { view: false, create: false, edit: false, delete: false }, residential_interests: { view: false, create: false, edit: false, delete: false, change_stage: false, }, }; // ─── Port Definitions ──────────────────────────────────────────────────────── const PORT_DEFINITIONS: Array<{ name: string; slug: string; primaryColor: string; defaultCurrency: string; timezone: string; }> = [ { name: 'Port Nimara', slug: 'port-nimara', primaryColor: '#0F4C81', defaultCurrency: 'USD', timezone: 'America/Anguilla', }, { name: 'Marina Azzurra', slug: 'marina-azzurra', primaryColor: '#2E86AB', defaultCurrency: 'EUR', timezone: 'Europe/Rome', }, { name: 'Harbor Royale', slug: 'harbor-royale', primaryColor: '#8B1E3F', defaultCurrency: 'GBP', timezone: 'Europe/London', }, ]; // ─── Seed Function ──────────────────────────────────────────────────────────── async function seed() { console.log('Seeding Port Nimara CRM...'); // ── 1. Ports ──────────────────────────────────────────────────────────────── console.log('Creating ports...'); const portIds: Array<{ id: string; name: string; slug: string }> = []; for (const def of PORT_DEFINITIONS) { const [inserted] = await db .insert(ports) .values({ id: crypto.randomUUID(), name: def.name, slug: def.slug, logoUrl: null, primaryColor: def.primaryColor, defaultCurrency: def.defaultCurrency, timezone: def.timezone, settings: {}, isActive: true, }) .onConflictDoNothing() .returning(); if (inserted) { console.log(` Port created: ${def.name} (${inserted.id})`); portIds.push({ id: inserted.id, name: def.name, slug: def.slug }); } else { // Port already existed — look it up so we can still seed fixtures for it. const [existing] = await db.select().from(ports).where(eq(ports.slug, def.slug)).limit(1); if (existing) { console.log(` Port exists: ${def.name} (${existing.id})`); portIds.push({ id: existing.id, name: def.name, slug: def.slug }); } else { console.warn(` Port insert conflict but lookup returned no row: ${def.slug}`); } } } // ── 2. System Roles ───────────────────────────────────────────────────────── console.log('Creating system roles...'); const systemRoles = [ { id: crypto.randomUUID(), name: 'super_admin', description: 'Full system access. Bypasses all permission checks.', permissions: ALL_PERMISSIONS, isGlobal: true, isSystem: true, }, { id: crypto.randomUUID(), name: 'director', description: 'Operational admin within assigned port(s). Can manage users and settings.', permissions: DIRECTOR_PERMISSIONS, isGlobal: true, isSystem: true, }, { id: crypto.randomUUID(), name: 'sales_manager', description: 'Full sales access. Can view all reminders, assign tasks, and export reports.', permissions: SALES_MANAGER_PERMISSIONS, isGlobal: true, isSystem: true, }, { id: crypto.randomUUID(), name: 'sales_agent', description: 'Standard sales role. View/create/edit clients and interests, manage own reminders.', permissions: SALES_AGENT_PERMISSIONS, isGlobal: true, isSystem: true, }, { id: crypto.randomUUID(), name: 'viewer', description: 'Read-only access to all records.', permissions: VIEWER_PERMISSIONS, isGlobal: true, isSystem: true, }, ]; for (const role of systemRoles) { await db.insert(roles).values(role).onConflictDoNothing(); console.log(` Role: ${role.name}`); } // ── 3. Super Admin User Profile ───────────────────────────────────────────── // Note: Better Auth creates the actual `user` record on first login. // We create the profile extension now, linked to a known user_id. // The Better Auth user_id for matt@portnimara.com must match this value // once Better Auth is configured. Use a stable placeholder ID here. console.log('Creating super admin user profile...'); const superAdminUserId = 'super-admin-matt-portnimara'; await db .insert(userProfiles) .values({ id: crypto.randomUUID(), userId: superAdminUserId, displayName: 'Matt', avatarUrl: null, phone: null, isSuperAdmin: true, isActive: true, lastLoginAt: null, preferences: {}, }) .onConflictDoNothing(); console.log(` Super admin profile for user_id: ${superAdminUserId}`); // ── 4. Per-port fixtures ──────────────────────────────────────────────────── console.log(''); console.log('Seeding per-port fixtures...'); const summaries: Array<{ name: string; summary: SeedSummary | null }> = []; for (const p of portIds) { console.log(` [${p.slug}] seeding fixture data...`); const summary = await seedPortData(p.id, p.slug); summaries.push({ name: p.name, summary }); } // ── 5. Summary ───────────────────────────────────────────────────────────── console.log(''); console.log('─── Summary ───────────────────────────────────────────────'); for (const s of summaries) { if (s.summary === null) { console.log(` ✓ Port "${s.name}" — already seeded (skipped)`); } else { const x = s.summary; console.log( ` ✓ Port "${s.name}" — ${x.berths} berths, ${x.clients} clients, ${x.companies} companies, ${x.yachts} yachts, ${x.interests} interests, ${x.reservations} reservations`, ); } } console.log(''); console.log('Seed complete!'); console.log(''); console.log('NOTE: The Better Auth user for matt@portnimara.com must be created'); console.log(`separately. Once created, update user_profiles.user_id to match`); console.log(`the actual Better Auth user ID (currently placeholder: ${superAdminUserId})`); process.exit(0); } seed().catch((err) => { console.error('Seed failed:', err); process.exit(1); });