/** * Dev-only: wipe all application data while keeping catalog / reference tables from seed. * * Preserved: organization_types, plans, features, permissions, treatment_types, * lab_workflow_steps, prosthesis_types, prosthesis_type_steps, catalog_translations * * Usage: npm run prisma:wipe-app-data */ import { PrismaClient } from '@prisma/client'; import { config } from 'dotenv'; import { existsSync, rmSync } from 'fs'; import path from 'path'; const envPath = path.join(__dirname, '..', '.env'); config({ path: envPath }); if (process.env.NODE_ENV === 'production') { console.error('wipe-app-data is not allowed in production'); process.exit(1); } const prisma = new PrismaClient(); const CATALOG_TABLES = new Set([ 'organization_types', 'plans', 'features', 'permissions', 'treatment_types', 'lab_workflow_steps', 'prosthesis_types', 'prosthesis_type_steps', 'catalog_translations', ]); // FK-safe order: children before parents where CASCADE is not enough. const TABLES_IN_ORDER = [ 'phone_verification_codes', 'staff_working_hours_blocks', 'staff_working_hours_schedules', 'staff_invitations', 'membership_permissions', 'sessions', 'lab_case_task_status_events', 'lab_case_comments', 'lab_case_attachments', 'lab_case_tasks', 'lab_case_sends', 'lab_case_tooth_prosthesis', 'lab_case_details', 'lab_cases', 'treatment_detail_attachments', 'treatment_details', 'treatments', 'appointments', 'organization_links', 'organization_invitations', 'patients', 'memberships', 'organizations', 'users', ]; async function tableExists(table: string): Promise { const rows = await prisma.$queryRawUnsafe>( `SELECT to_regclass('public."${table}"')::text AS exists`, ); return rows[0]?.exists != null; } async function main() { console.log('๐Ÿงน Wiping application data (keeping catalog / reference tables)...'); const existing: string[] = []; for (const table of TABLES_IN_ORDER) { if (CATALOG_TABLES.has(table)) { throw new Error(`Misconfigured wipe list includes catalog table: ${table}`); } if (await tableExists(table)) { existing.push(table); } else { console.log(` - skipping "${table}" (does not exist yet)`); } } if (existing.length === 0) { console.log('No application tables found. Run `prisma migrate deploy` first.'); return; } const targets = existing.map((t) => `"${t}"`).join(', '); await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${targets} RESTART IDENTITY CASCADE`); const uploadRoot = path.join(__dirname, '..', 'uploads'); if (existsSync(uploadRoot)) { rmSync(uploadRoot, { recursive: true, force: true }); console.log(' - removed local uploads/ directory'); } console.log('โœ… Application data wiped.'); console.log(' Preserved catalog tables:', [...CATALOG_TABLES].sort().join(', ')); console.log(' Register a new user / org to start fresh testing.'); } main() .catch((e) => { console.error('โŒ Wipe failed:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });