@javalabs/prisma-client 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.github/CODEOWNERS +1 -1
  2. package/README.md +269 -269
  3. package/migration-config.json +63 -63
  4. package/migration-config.json.bk +95 -95
  5. package/migrations/add_reserved_amount.sql +7 -7
  6. package/package.json +44 -44
  7. package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
  8. package/prisma/schema.prisma +609 -601
  9. package/src/index.ts +23 -23
  10. package/src/prisma-factory.service.ts +40 -40
  11. package/src/prisma.module.ts +9 -9
  12. package/src/prisma.service.ts +16 -16
  13. package/src/scripts/add-uuid-to-table.ts +138 -138
  14. package/src/scripts/create-tenant-schemas.ts +145 -145
  15. package/src/scripts/data-migration/batch-migrator.ts +248 -248
  16. package/src/scripts/data-migration/data-transformer.ts +426 -426
  17. package/src/scripts/data-migration/db-connector.ts +120 -120
  18. package/src/scripts/data-migration/dependency-resolver.ts +174 -174
  19. package/src/scripts/data-migration/entity-discovery.ts +196 -196
  20. package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
  21. package/src/scripts/data-migration/migration-config.json +63 -63
  22. package/src/scripts/data-migration/migration-tool.ts +509 -509
  23. package/src/scripts/data-migration/schema-utils.ts +248 -248
  24. package/src/scripts/data-migration/tenant-migrator.ts +201 -201
  25. package/src/scripts/data-migration/typecast-manager.ts +193 -193
  26. package/src/scripts/data-migration/types.ts +113 -113
  27. package/src/scripts/database-initializer.ts +49 -49
  28. package/src/scripts/drop-database.ts +104 -104
  29. package/src/scripts/dump-source-db.sh +61 -61
  30. package/src/scripts/encrypt-user-passwords.ts +36 -36
  31. package/src/scripts/error-handler.ts +117 -117
  32. package/src/scripts/fix-data-types.ts +241 -241
  33. package/src/scripts/fix-enum-values.ts +357 -357
  34. package/src/scripts/fix-schema-discrepancies.ts +317 -317
  35. package/src/scripts/fix-table-indexes.ts +601 -601
  36. package/src/scripts/migrate-schema-structure.ts +90 -90
  37. package/src/scripts/migrate-uuid.ts +76 -76
  38. package/src/scripts/post-migration-validator.ts +526 -526
  39. package/src/scripts/pre-migration-validator.ts +610 -610
  40. package/src/scripts/reset-database.ts +263 -263
  41. package/src/scripts/retry-failed-migrations.ts +416 -416
  42. package/src/scripts/run-migration.ts +707 -707
  43. package/src/scripts/schema-sync.ts +128 -128
  44. package/src/scripts/sequence-sync-cli.ts +416 -416
  45. package/src/scripts/sequence-synchronizer.ts +127 -127
  46. package/src/scripts/sync-enum-types.ts +170 -170
  47. package/src/scripts/sync-enum-values.ts +563 -563
  48. package/src/scripts/truncate-database.ts +123 -123
  49. package/src/scripts/verify-migration-setup.ts +135 -135
  50. package/tsconfig.json +17 -17
@@ -1,707 +1,707 @@
1
- import { Command } from "commander";
2
- import * as dotenv from "dotenv";
3
- import { exec } from "child_process";
4
- import { promisify } from "util";
5
- import { SchemaDiscrepancyFixer } from "./fix-schema-discrepancies";
6
- import { EnumSynchronizer } from "./sync-enum-types";
7
- import { PrismaClient } from "@prisma/client";
8
- import { MigrationConfig } from "./data-migration/types";
9
- import * as fs from "fs";
10
- import * as path from "path";
11
- import { DatabaseInitializer } from "./database-initializer";
12
- import { SequenceSynchronizer } from "./sequence-synchronizer";
13
-
14
- dotenv.config();
15
-
16
- const execAsync = promisify(exec);
17
- const program = new Command();
18
-
19
- // Función para cargar la configuración de migración
20
- async function loadMigrationConfig(
21
- configPath?: string
22
- ): Promise<MigrationConfig> {
23
- const defaultPath = path.join(
24
- __dirname,
25
- "data-migration",
26
- "migration-config.json"
27
- );
28
- const filePath = configPath || defaultPath;
29
-
30
- try {
31
- const configContent = await fs.promises.readFile(filePath, "utf8");
32
- return JSON.parse(configContent);
33
- } catch (error) {
34
- console.error(`Error loading migration config from ${filePath}:`, error);
35
- throw error;
36
- }
37
- }
38
-
39
- // Añadir esta función después de las importaciones
40
- async function testDatabaseConnection(
41
- url: string,
42
- name: string
43
- ): Promise<boolean> {
44
- const { Pool } = await import("pg");
45
- const pool = new Pool({ connectionString: url });
46
-
47
- try {
48
- console.log(`Testing connection to ${name} database...`);
49
- const client = await pool.connect();
50
- const result = await client.query("SELECT NOW()");
51
- client.release();
52
-
53
- console.log(`Successfully connected to ${name} database`);
54
- return true;
55
- } catch (error) {
56
- console.error(`Failed to connect to ${name} database: ${error.message}`);
57
- return false;
58
- } finally {
59
- await pool.end();
60
- }
61
- }
62
-
63
- // Modificar la acción del comando migrate para incluir la prueba de conexión
64
- program
65
- .command("migrate")
66
- .description("Run the data migration process")
67
- .option("-s, --source <url>", "Source database connection URL")
68
- .option("-t, --target <url>", "Target database connection URL")
69
- .option(
70
- "-m, --mode <mode>",
71
- "Migration mode: 'multi-tenant' or 'public-only'",
72
- "multi-tenant"
73
- )
74
- .option("-d, --dry-run", "Perform a dry run without making changes", false)
75
- .option("--skip-schema-creation", "Skip the schema creation step", false)
76
- .option("--skip-schema-migration", "Skip the schema migration step", false)
77
- .option("--skip-data-migration", "Skip the data migration step", false)
78
- .option("--skip-validation", "Skip pre and post validation steps", false)
79
- .option("--skip-enum-sync", "Skip enum synchronization step", false)
80
- .option("--auto-fix-schema", "Automatically fix schema discrepancies", false)
81
- .option("--force", "Force migration even if validation fails", false)
82
- .option("-y, --yes", "Automatically answer yes to all prompts", false)
83
- .option("--config-path <path>", "Path to migration config file")
84
- .option("--skip-initialization", "Skip database initialization step", false)
85
- .option("--skip-sequence-sync", "Skip sequence synchronization step", false)
86
- .action(async (options) => {
87
- // Set environment variables for the migration
88
- if (options.source) {
89
- process.env.SOURCE_DATABASE_URL = options.source;
90
- }
91
-
92
- if (options.target) {
93
- process.env.DATABASE_URL = options.target;
94
- }
95
-
96
- if (!process.env.SOURCE_DATABASE_URL) {
97
- console.error(
98
- "Source database URL is required. Use --source or set SOURCE_DATABASE_URL environment variable."
99
- );
100
- process.exit(1);
101
- }
102
-
103
- if (!process.env.DATABASE_URL) {
104
- console.error(
105
- "Target database URL is required. Use --target or set DATABASE_URL environment variable."
106
- );
107
- process.exit(1);
108
- }
109
-
110
- if (options.dryRun) {
111
- console.log(
112
- "Performing dry run - no changes will be made to the database"
113
- );
114
- process.env.DRY_RUN = "true";
115
- }
116
-
117
- const sourceConnected = await testDatabaseConnection(
118
- process.env.SOURCE_DATABASE_URL,
119
- "source"
120
- );
121
- const targetConnected = await testDatabaseConnection(
122
- process.env.DATABASE_URL,
123
- "target"
124
- );
125
-
126
- if (!sourceConnected || !targetConnected) {
127
- console.error(
128
- "Database connection test failed. Please check your connection strings and try again."
129
- );
130
- process.exit(1);
131
- }
132
-
133
- try {
134
- // Step 0: Initialize database if needed
135
- if (!options.skipInitialization) {
136
- console.log("Step 0: Checking database initialization...");
137
- const initializer = new DatabaseInitializer(process.env.DATABASE_URL);
138
- await initializer.initialize();
139
- }
140
-
141
- // Step 1: Pre-migration validation
142
- if (!options.skipValidation) {
143
- console.log("Step 1: Running pre-migration validation...");
144
- const { PreMigrationValidator } = await import(
145
- "./pre-migration-validator"
146
- );
147
- const validator = new PreMigrationValidator();
148
- const validationResult = await validator.validate();
149
-
150
- if (!validationResult.success && !options.force) {
151
- console.log(
152
- `Pre-migration validation found ${validationResult.issueCount} issues.`
153
- );
154
- console.log(JSON.stringify(validationResult, null, 2));
155
-
156
- // Si la opción --yes o --force está activada, continuar automáticamente
157
- if (options.yes) {
158
- console.log(
159
- "Continuing with migration despite issues (--yes flag provided)"
160
- );
161
- } else if (!options.force) {
162
- // Preguntar al usuario si desea continuar a pesar de los problemas
163
- const readline = require("readline").createInterface({
164
- input: process.stdin,
165
- output: process.stdout,
166
- });
167
-
168
- const answer = await new Promise<string>((resolve) => {
169
- readline.question(
170
- "Continue with migration despite issues? (y/N) ",
171
- (ans: string) => {
172
- resolve(ans);
173
- readline.close();
174
- }
175
- );
176
- });
177
-
178
- if (answer.toLowerCase() !== "y") {
179
- console.log("Migration aborted by user.");
180
- process.exit(1);
181
- }
182
- }
183
- } else {
184
- console.log("Pre-migration validation successful!");
185
- }
186
-
187
- // Corregir automáticamente las discrepancias del esquema si se solicita
188
- if (options.autoFixSchema && validationResult.reportPath) {
189
- console.log("Automatically fixing schema discrepancies...");
190
- const fixer = new SchemaDiscrepancyFixer(
191
- process.env.SOURCE_DATABASE_URL,
192
- process.env.DATABASE_URL,
193
- validationResult.reportPath
194
- );
195
- await fixer.fixDiscrepancies();
196
- }
197
- }
198
-
199
- // Step 2: Migrate schema structure
200
- if (!options.skipSchemaMigration) {
201
- console.log("Step 2: Migrating schema structure...");
202
- if (options.mode === "multi-tenant") {
203
- await execAsync("node dist/scripts/migrate-schema-structure.js");
204
- } else {
205
- const { Pool } = await import("pg");
206
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
207
-
208
- // Helper function to check if tables exist
209
- async function checkIfTablesExist(pool: any): Promise<boolean> {
210
- try {
211
- const result = await pool.query(`
212
- SELECT COUNT(*)
213
- FROM information_schema.tables
214
- WHERE table_schema = 'public'
215
- AND table_type = 'BASE TABLE'
216
- `);
217
-
218
- return parseInt(result.rows[0].count) > 0;
219
- } catch (error) {
220
- console.error("Error checking tables:", error);
221
- return false;
222
- }
223
- }
224
-
225
- const tablesExist = await checkIfTablesExist(pool);
226
- if (!tablesExist) {
227
- await execAsync("npx prisma migrate deploy");
228
- }
229
- }
230
- }
231
-
232
- // Step 3: Synchronize and fix enum types
233
- if (!options.skipEnumSync) {
234
- console.log("Step 3: Synchronizing and fixing enum types...");
235
- // Sync enum types
236
- const { EnumSynchronizer } = await import("./sync-enum-types");
237
- const synchronizer = new EnumSynchronizer();
238
- await synchronizer.synchronizeEnums();
239
- console.log("Enum types synchronized successfully!");
240
-
241
- // Sync enum values
242
- console.log("Step 3.1: Synchronizing enum values...");
243
- const { EnumValueSynchronizer } = await import("./sync-enum-values");
244
- const valueSynchronizer = new EnumValueSynchronizer();
245
- await valueSynchronizer.synchronizeEnumValues();
246
- console.log("Enum values synchronized successfully!");
247
-
248
- // Fix enum values
249
- console.log("Step 3.2: Fixing enum values...");
250
- const { EnumFixer } = await import("./fix-enum-values");
251
- const enumFixer = new EnumFixer(process.env.DATABASE_URL);
252
- await enumFixer.fixEnumValues();
253
- console.log("Enum values fixed successfully!");
254
- }
255
-
256
- // Step 4: Fix data types
257
- console.log("Step 4: Fixing data type issues...");
258
- const { DataTypeFixer } = await import("./fix-data-types");
259
- const fixer = new DataTypeFixer();
260
- await fixer.fixDataTypes();
261
- console.log("Data type fixing completed successfully!");
262
-
263
- // Step 5: Migrate data with transformation
264
- if (!options.skipDataMigration) {
265
- console.log("Step 5: Migrating data with transformation...");
266
- const { DataMigrationTool } = await import(
267
- "./data-migration/migration-tool"
268
- );
269
- const { PrismaClient } = await import("@prisma/client");
270
- const { Pool } = await import("pg");
271
-
272
- // Cargar la configuración de migración
273
- const migrationConfig = await loadMigrationConfig(options.configPath);
274
-
275
- // Create connections with increased pool size and timeout
276
- const connections = {
277
- sourcePool: new Pool({
278
- connectionString: process.env.SOURCE_DATABASE_URL,
279
- max: 20,
280
- idleTimeoutMillis: 30000,
281
- }),
282
- targetPool: new Pool({
283
- connectionString: process.env.DATABASE_URL,
284
- max: 20,
285
- idleTimeoutMillis: 30000,
286
- }),
287
- sourcePrisma: new PrismaClient({
288
- datasources: { db: { url: process.env.SOURCE_DATABASE_URL } },
289
- log: ["error", "warn"],
290
- }),
291
- targetPrisma: new PrismaClient({
292
- datasources: { db: { url: process.env.DATABASE_URL } },
293
- log: ["error", "warn"],
294
- }),
295
- };
296
-
297
- try {
298
- // Disable foreign key constraints before migration
299
- await connections.targetPool.query(
300
- "SET session_replication_role = 'replica';"
301
- );
302
-
303
- // Si es public-only, modificar la configuración para solo incluir tablas públicas
304
- if (options.mode === "public-only") {
305
- migrationConfig.tables = Object.entries(migrationConfig.tables)
306
- .filter(([_, config]) => config.type === "public")
307
- .reduce(
308
- (acc, [key, value]) => ({
309
- ...acc,
310
- [key]: value,
311
- }),
312
- {}
313
- );
314
- }
315
-
316
- const migrationTool = new DataMigrationTool(
317
- connections,
318
- {
319
- ...migrationConfig,
320
- tables: Object.entries(migrationConfig.tables).reduce(
321
- (acc, [key, value]) => ({
322
- ...acc,
323
- [key]: {
324
- ...value,
325
- sourceTable: value.sourceTable || key,
326
- targetTable: value.targetTable || key,
327
- },
328
- }),
329
- {}
330
- ),
331
- },
332
- {
333
- publicOnly: options.mode === "public-only",
334
- targetSchema: "public",
335
- sourceSchema: "public",
336
- multiTenant: options.mode !== "public-only",
337
- }
338
- );
339
-
340
- await migrationTool.migrate();
341
- } finally {
342
- // Re-enable foreign key constraints
343
- await connections.targetPool.query(
344
- "SET session_replication_role = 'origin';"
345
- );
346
-
347
- // Close connections
348
- await connections.sourcePool.end();
349
- await connections.targetPool.end();
350
- await connections.sourcePrisma.$disconnect();
351
- await connections.targetPrisma.$disconnect();
352
- }
353
- }
354
-
355
- // Step 6: Post-migration validation
356
- if (!options.skipValidation) {
357
- console.log("Step 6: Running post-migration validation...");
358
- const { PostMigrationValidator } = await import(
359
- "./post-migration-validator"
360
- );
361
- const validator = new PostMigrationValidator();
362
- const result = await validator.validate({
363
- publicOnly: options.mode === "public-only",
364
- });
365
-
366
- if (result.success) {
367
- console.log("Post-migration validation successful!");
368
- } else {
369
- console.log(
370
- `Post-migration validation found ${result.issueCount} issues.`
371
- );
372
- console.log(JSON.stringify(result, null, 2));
373
-
374
- if (!options.force) {
375
- console.error(
376
- "Migration validation failed. Use --force to continue anyway."
377
- );
378
- process.exit(1);
379
- }
380
- console.log(
381
- "Continuing with migration despite validation issues (--force flag provided)"
382
- );
383
- }
384
- }
385
-
386
- // Step 7: Synchronize sequences (final step)
387
- if (!options.skipSequenceSync) {
388
- console.log("Step 7: Synchronizing database sequences...");
389
- const sequenceSynchronizer = new SequenceSynchronizer(
390
- process.env.DATABASE_URL
391
- );
392
- await sequenceSynchronizer.synchronizeSequences();
393
- console.log("Database sequences synchronized successfully!");
394
- }
395
-
396
- console.log("Migration process completed successfully!");
397
- } catch (error) {
398
- console.error("Error running migration:", error);
399
- process.exit(1);
400
- }
401
- });
402
-
403
- program
404
- .command("fix-data-types")
405
- .description("Fix data type issues in the migrated data")
406
- .option("-t, --target <url>", "Target database connection URL")
407
- .action(async (options) => {
408
- if (options.target) {
409
- process.env.DATABASE_URL = options.target;
410
- }
411
-
412
- if (!process.env.DATABASE_URL) {
413
- console.error(
414
- "Target database URL is required. Use --target or set DATABASE_URL environment variable."
415
- );
416
- process.exit(1);
417
- }
418
-
419
- try {
420
- console.log("Fixing data type issues...");
421
- const { DataTypeFixer } = await import("./fix-data-types");
422
- const fixer = new DataTypeFixer();
423
- await fixer.fixDataTypes();
424
- console.log("Data type fixing completed successfully!");
425
- } catch (error) {
426
- console.error("Error fixing data types:", error);
427
- process.exit(1);
428
- }
429
- });
430
-
431
- program
432
- .command("truncate-database")
433
- .description(
434
- "Truncate (empty) all tables in the database without dropping the database"
435
- )
436
- .option("-t, --target <url>", "Target database connection URL")
437
- .action(async (options) => {
438
- if (options.target) {
439
- process.env.DATABASE_URL = options.target;
440
- }
441
-
442
- if (!process.env.DATABASE_URL) {
443
- console.error(
444
- "Target database URL is required. Use --target or set DATABASE_URL environment variable."
445
- );
446
- process.exit(1);
447
- }
448
-
449
- try {
450
- console.log("Truncating database...");
451
- const { DatabaseTruncateTool } = await import("./truncate-database");
452
- const truncateTool = new DatabaseTruncateTool(process.env.DATABASE_URL);
453
- await truncateTool.truncateDatabase();
454
- console.log("Database truncation completed successfully!");
455
- } catch (error) {
456
- console.error("Error truncating database:", error);
457
- process.exit(1);
458
- }
459
- });
460
-
461
- program
462
- .command("retry-failed")
463
- .description("Retry failed migrations from an error log file")
464
- .argument("<error-log-path>", "Path to the error log file")
465
- .option("-t, --target <url>", "Target database connection URL")
466
- .action(async (errorLogPath, options) => {
467
- if (options.target) {
468
- process.env.DATABASE_URL = options.target;
469
- }
470
-
471
- if (!process.env.DATABASE_URL) {
472
- console.error(
473
- "Target database URL is required. Use --target or set DATABASE_URL environment variable."
474
- );
475
- process.exit(1);
476
- }
477
-
478
- try {
479
- console.log(`Retrying failed migrations from log: ${errorLogPath}`);
480
- const { FailedMigrationRetry } = await import(
481
- "./retry-failed-migrations"
482
- );
483
- const retryTool = new FailedMigrationRetry(errorLogPath);
484
- await retryTool.retryFailedMigrations();
485
- console.log("Retry process completed!");
486
- } catch (error) {
487
- console.error("Error retrying failed migrations:", error);
488
- process.exit(1);
489
- }
490
- });
491
-
492
- program
493
- .command("fix-enum-values")
494
- .description("Fix invalid enum values in the database")
495
- .option("-t, --target <url>", "Target database connection URL")
496
- .action(async (options) => {
497
- if (options.target) {
498
- process.env.DATABASE_URL = options.target;
499
- }
500
-
501
- if (!process.env.DATABASE_URL) {
502
- console.error(
503
- "Target database URL is required. Use --target or set DATABASE_URL environment variable."
504
- );
505
- process.exit(1);
506
- }
507
-
508
- try {
509
- console.log("Fixing enum values...");
510
- const { EnumFixer } = await import("./fix-enum-values");
511
- const enumFixer = new EnumFixer(process.env.DATABASE_URL);
512
- await enumFixer.fixEnumValues();
513
- console.log("Enum values fixed successfully!");
514
- } catch (error) {
515
- console.error("Error fixing enum values:", error);
516
- process.exit(1);
517
- }
518
- });
519
-
520
- program
521
- .command("pre-validate")
522
- .description("Validate database before migration")
523
- .option("-s, --source <url>", "Source database connection URL")
524
- .option("-t, --target <url>", "Target database connection URL")
525
- .option("-y, --yes", "Automatically answer yes to all prompts", false)
526
- .action(async (options) => {
527
- if (options.source) {
528
- process.env.SOURCE_DATABASE_URL = options.source;
529
- }
530
- if (options.target) {
531
- process.env.DATABASE_URL = options.target;
532
- }
533
-
534
- if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
535
- console.error(
536
- "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
537
- );
538
- process.exit(1);
539
- }
540
-
541
- try {
542
- console.log("Validating database before migration...");
543
- const { PreMigrationValidator } = await import(
544
- "./pre-migration-validator"
545
- );
546
- const validator = new PreMigrationValidator();
547
- const result = await validator.validate();
548
-
549
- if (result.success) {
550
- console.log("Pre-migration validation successful!");
551
- process.exit(0);
552
- } else {
553
- console.log(
554
- `Pre-migration validation found ${result.issueCount} issues.`
555
- );
556
- console.log(JSON.stringify(result, null, 2));
557
-
558
- // Si la opción --yes está activada, continuar automáticamente
559
- if (options.yes) {
560
- console.log(
561
- "Continuing with migration despite issues (--yes flag provided)"
562
- );
563
- process.exit(0);
564
- }
565
-
566
- // Arreglar el problema con readline usando una promesa
567
- const readline = require("readline").createInterface({
568
- input: process.stdin,
569
- output: process.stdout,
570
- });
571
-
572
- const answer = await new Promise<string>((resolve) => {
573
- readline.question(
574
- "Continue with migration despite issues? (y/N) ",
575
- (ans: string) => {
576
- resolve(ans);
577
- readline.close();
578
- }
579
- );
580
- });
581
-
582
- if (answer.toLowerCase() === "y") {
583
- console.log("Continuing with migration despite issues.");
584
- process.exit(0);
585
- } else {
586
- console.log("Migration aborted by user.");
587
- process.exit(1);
588
- }
589
- }
590
- } catch (error) {
591
- console.error("Error during pre-migration validation:", error);
592
- process.exit(1);
593
- }
594
- });
595
-
596
- program
597
- .command("sync-enums")
598
- .description("Synchronize enum types between databases")
599
- .option("-s, --source <url>", "Source database connection URL")
600
- .option("-t, --target <url>", "Target database connection URL")
601
- .action(async (options) => {
602
- if (options.source) {
603
- process.env.SOURCE_DATABASE_URL = options.source;
604
- }
605
- if (options.target) {
606
- process.env.DATABASE_URL = options.target;
607
- }
608
-
609
- if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
610
- console.error(
611
- "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
612
- );
613
- process.exit(1);
614
- }
615
-
616
- try {
617
- console.log("Synchronizing enum types...");
618
- const { EnumSynchronizer } = await import("./sync-enum-types");
619
- const synchronizer = new EnumSynchronizer();
620
- await synchronizer.synchronizeEnums();
621
- console.log("Enum types synchronized successfully!");
622
- } catch (error) {
623
- console.error("Error synchronizing enum types:", error);
624
- process.exit(1);
625
- }
626
- });
627
-
628
- program
629
- .command("post-validate")
630
- .description("Validate database after migration")
631
- .option("-s, --source <url>", "Source database connection URL")
632
- .option("-t, --target <url>", "Target database connection URL")
633
- .action(async (options) => {
634
- if (options.source) {
635
- process.env.SOURCE_DATABASE_URL = options.source;
636
- }
637
- if (options.target) {
638
- process.env.DATABASE_URL = options.target;
639
- }
640
-
641
- if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
642
- console.error(
643
- "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
644
- );
645
- process.exit(1);
646
- }
647
-
648
- try {
649
- console.log("Validating database after migration...");
650
- const { PostMigrationValidator } = await import(
651
- "./post-migration-validator"
652
- );
653
- const validator = new PostMigrationValidator();
654
- const result = await validator.validate();
655
-
656
- if (result.success) {
657
- console.log("Post-migration validation successful!");
658
- } else {
659
- console.log(
660
- `Post-migration validation found ${result.issueCount} issues.`
661
- );
662
- console.log(JSON.stringify(result, null, 2));
663
- }
664
- } catch (error) {
665
- console.error("Error during post-migration validation:", error);
666
- process.exit(1);
667
- }
668
- });
669
-
670
- program
671
- .command("verify")
672
- .description("Verify migration setup and connections")
673
- .action(async () => {
674
- try {
675
- const { MigrationSetupVerifier } = await import(
676
- "./verify-migration-setup"
677
- );
678
- const verifier = new MigrationSetupVerifier();
679
- await verifier.verifySetup();
680
- } catch (error) {
681
- console.error("Verification failed:", error);
682
- process.exit(1);
683
- }
684
- });
685
-
686
- program
687
- .command("fix-schema")
688
- .description("Fix schema discrepancies between source and target databases")
689
- .option("-r, --report <path>", "Path to pre-migration report file")
690
- .action(async (options) => {
691
- try {
692
- const fixer = new SchemaDiscrepancyFixer(
693
- process.env.SOURCE_DATABASE_URL,
694
- process.env.DATABASE_URL,
695
- options.report
696
- );
697
-
698
- await fixer.fixDiscrepancies();
699
- console.log("Schema discrepancies fixed successfully");
700
- } catch (error) {
701
- console.error("Error fixing schema discrepancies:", error.message);
702
- process.exit(1);
703
- }
704
- });
705
-
706
- // Remove the unused run() function or call it if needed
707
- program.parse();
1
+ import { Command } from "commander";
2
+ import * as dotenv from "dotenv";
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import { SchemaDiscrepancyFixer } from "./fix-schema-discrepancies";
6
+ import { EnumSynchronizer } from "./sync-enum-types";
7
+ import { PrismaClient } from "@prisma/client";
8
+ import { MigrationConfig } from "./data-migration/types";
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import { DatabaseInitializer } from "./database-initializer";
12
+ import { SequenceSynchronizer } from "./sequence-synchronizer";
13
+
14
+ dotenv.config();
15
+
16
+ const execAsync = promisify(exec);
17
+ const program = new Command();
18
+
19
+ // Función para cargar la configuración de migración
20
+ async function loadMigrationConfig(
21
+ configPath?: string
22
+ ): Promise<MigrationConfig> {
23
+ const defaultPath = path.join(
24
+ __dirname,
25
+ "data-migration",
26
+ "migration-config.json"
27
+ );
28
+ const filePath = configPath || defaultPath;
29
+
30
+ try {
31
+ const configContent = await fs.promises.readFile(filePath, "utf8");
32
+ return JSON.parse(configContent);
33
+ } catch (error) {
34
+ console.error(`Error loading migration config from ${filePath}:`, error);
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ // Añadir esta función después de las importaciones
40
+ async function testDatabaseConnection(
41
+ url: string,
42
+ name: string
43
+ ): Promise<boolean> {
44
+ const { Pool } = await import("pg");
45
+ const pool = new Pool({ connectionString: url });
46
+
47
+ try {
48
+ console.log(`Testing connection to ${name} database...`);
49
+ const client = await pool.connect();
50
+ const result = await client.query("SELECT NOW()");
51
+ client.release();
52
+
53
+ console.log(`Successfully connected to ${name} database`);
54
+ return true;
55
+ } catch (error) {
56
+ console.error(`Failed to connect to ${name} database: ${error.message}`);
57
+ return false;
58
+ } finally {
59
+ await pool.end();
60
+ }
61
+ }
62
+
63
+ // Modificar la acción del comando migrate para incluir la prueba de conexión
64
+ program
65
+ .command("migrate")
66
+ .description("Run the data migration process")
67
+ .option("-s, --source <url>", "Source database connection URL")
68
+ .option("-t, --target <url>", "Target database connection URL")
69
+ .option(
70
+ "-m, --mode <mode>",
71
+ "Migration mode: 'multi-tenant' or 'public-only'",
72
+ "multi-tenant"
73
+ )
74
+ .option("-d, --dry-run", "Perform a dry run without making changes", false)
75
+ .option("--skip-schema-creation", "Skip the schema creation step", false)
76
+ .option("--skip-schema-migration", "Skip the schema migration step", false)
77
+ .option("--skip-data-migration", "Skip the data migration step", false)
78
+ .option("--skip-validation", "Skip pre and post validation steps", false)
79
+ .option("--skip-enum-sync", "Skip enum synchronization step", false)
80
+ .option("--auto-fix-schema", "Automatically fix schema discrepancies", false)
81
+ .option("--force", "Force migration even if validation fails", false)
82
+ .option("-y, --yes", "Automatically answer yes to all prompts", false)
83
+ .option("--config-path <path>", "Path to migration config file")
84
+ .option("--skip-initialization", "Skip database initialization step", false)
85
+ .option("--skip-sequence-sync", "Skip sequence synchronization step", false)
86
+ .action(async (options) => {
87
+ // Set environment variables for the migration
88
+ if (options.source) {
89
+ process.env.SOURCE_DATABASE_URL = options.source;
90
+ }
91
+
92
+ if (options.target) {
93
+ process.env.DATABASE_URL = options.target;
94
+ }
95
+
96
+ if (!process.env.SOURCE_DATABASE_URL) {
97
+ console.error(
98
+ "Source database URL is required. Use --source or set SOURCE_DATABASE_URL environment variable."
99
+ );
100
+ process.exit(1);
101
+ }
102
+
103
+ if (!process.env.DATABASE_URL) {
104
+ console.error(
105
+ "Target database URL is required. Use --target or set DATABASE_URL environment variable."
106
+ );
107
+ process.exit(1);
108
+ }
109
+
110
+ if (options.dryRun) {
111
+ console.log(
112
+ "Performing dry run - no changes will be made to the database"
113
+ );
114
+ process.env.DRY_RUN = "true";
115
+ }
116
+
117
+ const sourceConnected = await testDatabaseConnection(
118
+ process.env.SOURCE_DATABASE_URL,
119
+ "source"
120
+ );
121
+ const targetConnected = await testDatabaseConnection(
122
+ process.env.DATABASE_URL,
123
+ "target"
124
+ );
125
+
126
+ if (!sourceConnected || !targetConnected) {
127
+ console.error(
128
+ "Database connection test failed. Please check your connection strings and try again."
129
+ );
130
+ process.exit(1);
131
+ }
132
+
133
+ try {
134
+ // Step 0: Initialize database if needed
135
+ if (!options.skipInitialization) {
136
+ console.log("Step 0: Checking database initialization...");
137
+ const initializer = new DatabaseInitializer(process.env.DATABASE_URL);
138
+ await initializer.initialize();
139
+ }
140
+
141
+ // Step 1: Pre-migration validation
142
+ if (!options.skipValidation) {
143
+ console.log("Step 1: Running pre-migration validation...");
144
+ const { PreMigrationValidator } = await import(
145
+ "./pre-migration-validator"
146
+ );
147
+ const validator = new PreMigrationValidator();
148
+ const validationResult = await validator.validate();
149
+
150
+ if (!validationResult.success && !options.force) {
151
+ console.log(
152
+ `Pre-migration validation found ${validationResult.issueCount} issues.`
153
+ );
154
+ console.log(JSON.stringify(validationResult, null, 2));
155
+
156
+ // Si la opción --yes o --force está activada, continuar automáticamente
157
+ if (options.yes) {
158
+ console.log(
159
+ "Continuing with migration despite issues (--yes flag provided)"
160
+ );
161
+ } else if (!options.force) {
162
+ // Preguntar al usuario si desea continuar a pesar de los problemas
163
+ const readline = require("readline").createInterface({
164
+ input: process.stdin,
165
+ output: process.stdout,
166
+ });
167
+
168
+ const answer = await new Promise<string>((resolve) => {
169
+ readline.question(
170
+ "Continue with migration despite issues? (y/N) ",
171
+ (ans: string) => {
172
+ resolve(ans);
173
+ readline.close();
174
+ }
175
+ );
176
+ });
177
+
178
+ if (answer.toLowerCase() !== "y") {
179
+ console.log("Migration aborted by user.");
180
+ process.exit(1);
181
+ }
182
+ }
183
+ } else {
184
+ console.log("Pre-migration validation successful!");
185
+ }
186
+
187
+ // Corregir automáticamente las discrepancias del esquema si se solicita
188
+ if (options.autoFixSchema && validationResult.reportPath) {
189
+ console.log("Automatically fixing schema discrepancies...");
190
+ const fixer = new SchemaDiscrepancyFixer(
191
+ process.env.SOURCE_DATABASE_URL,
192
+ process.env.DATABASE_URL,
193
+ validationResult.reportPath
194
+ );
195
+ await fixer.fixDiscrepancies();
196
+ }
197
+ }
198
+
199
+ // Step 2: Migrate schema structure
200
+ if (!options.skipSchemaMigration) {
201
+ console.log("Step 2: Migrating schema structure...");
202
+ if (options.mode === "multi-tenant") {
203
+ await execAsync("node dist/scripts/migrate-schema-structure.js");
204
+ } else {
205
+ const { Pool } = await import("pg");
206
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
207
+
208
+ // Helper function to check if tables exist
209
+ async function checkIfTablesExist(pool: any): Promise<boolean> {
210
+ try {
211
+ const result = await pool.query(`
212
+ SELECT COUNT(*)
213
+ FROM information_schema.tables
214
+ WHERE table_schema = 'public'
215
+ AND table_type = 'BASE TABLE'
216
+ `);
217
+
218
+ return parseInt(result.rows[0].count) > 0;
219
+ } catch (error) {
220
+ console.error("Error checking tables:", error);
221
+ return false;
222
+ }
223
+ }
224
+
225
+ const tablesExist = await checkIfTablesExist(pool);
226
+ if (!tablesExist) {
227
+ await execAsync("npx prisma migrate deploy");
228
+ }
229
+ }
230
+ }
231
+
232
+ // Step 3: Synchronize and fix enum types
233
+ if (!options.skipEnumSync) {
234
+ console.log("Step 3: Synchronizing and fixing enum types...");
235
+ // Sync enum types
236
+ const { EnumSynchronizer } = await import("./sync-enum-types");
237
+ const synchronizer = new EnumSynchronizer();
238
+ await synchronizer.synchronizeEnums();
239
+ console.log("Enum types synchronized successfully!");
240
+
241
+ // Sync enum values
242
+ console.log("Step 3.1: Synchronizing enum values...");
243
+ const { EnumValueSynchronizer } = await import("./sync-enum-values");
244
+ const valueSynchronizer = new EnumValueSynchronizer();
245
+ await valueSynchronizer.synchronizeEnumValues();
246
+ console.log("Enum values synchronized successfully!");
247
+
248
+ // Fix enum values
249
+ console.log("Step 3.2: Fixing enum values...");
250
+ const { EnumFixer } = await import("./fix-enum-values");
251
+ const enumFixer = new EnumFixer(process.env.DATABASE_URL);
252
+ await enumFixer.fixEnumValues();
253
+ console.log("Enum values fixed successfully!");
254
+ }
255
+
256
+ // Step 4: Fix data types
257
+ console.log("Step 4: Fixing data type issues...");
258
+ const { DataTypeFixer } = await import("./fix-data-types");
259
+ const fixer = new DataTypeFixer();
260
+ await fixer.fixDataTypes();
261
+ console.log("Data type fixing completed successfully!");
262
+
263
+ // Step 5: Migrate data with transformation
264
+ if (!options.skipDataMigration) {
265
+ console.log("Step 5: Migrating data with transformation...");
266
+ const { DataMigrationTool } = await import(
267
+ "./data-migration/migration-tool"
268
+ );
269
+ const { PrismaClient } = await import("@prisma/client");
270
+ const { Pool } = await import("pg");
271
+
272
+ // Cargar la configuración de migración
273
+ const migrationConfig = await loadMigrationConfig(options.configPath);
274
+
275
+ // Create connections with increased pool size and timeout
276
+ const connections = {
277
+ sourcePool: new Pool({
278
+ connectionString: process.env.SOURCE_DATABASE_URL,
279
+ max: 20,
280
+ idleTimeoutMillis: 30000,
281
+ }),
282
+ targetPool: new Pool({
283
+ connectionString: process.env.DATABASE_URL,
284
+ max: 20,
285
+ idleTimeoutMillis: 30000,
286
+ }),
287
+ sourcePrisma: new PrismaClient({
288
+ datasources: { db: { url: process.env.SOURCE_DATABASE_URL } },
289
+ log: ["error", "warn"],
290
+ }),
291
+ targetPrisma: new PrismaClient({
292
+ datasources: { db: { url: process.env.DATABASE_URL } },
293
+ log: ["error", "warn"],
294
+ }),
295
+ };
296
+
297
+ try {
298
+ // Disable foreign key constraints before migration
299
+ await connections.targetPool.query(
300
+ "SET session_replication_role = 'replica';"
301
+ );
302
+
303
+ // Si es public-only, modificar la configuración para solo incluir tablas públicas
304
+ if (options.mode === "public-only") {
305
+ migrationConfig.tables = Object.entries(migrationConfig.tables)
306
+ .filter(([_, config]) => config.type === "public")
307
+ .reduce(
308
+ (acc, [key, value]) => ({
309
+ ...acc,
310
+ [key]: value,
311
+ }),
312
+ {}
313
+ );
314
+ }
315
+
316
+ const migrationTool = new DataMigrationTool(
317
+ connections,
318
+ {
319
+ ...migrationConfig,
320
+ tables: Object.entries(migrationConfig.tables).reduce(
321
+ (acc, [key, value]) => ({
322
+ ...acc,
323
+ [key]: {
324
+ ...value,
325
+ sourceTable: value.sourceTable || key,
326
+ targetTable: value.targetTable || key,
327
+ },
328
+ }),
329
+ {}
330
+ ),
331
+ },
332
+ {
333
+ publicOnly: options.mode === "public-only",
334
+ targetSchema: "public",
335
+ sourceSchema: "public",
336
+ multiTenant: options.mode !== "public-only",
337
+ }
338
+ );
339
+
340
+ await migrationTool.migrate();
341
+ } finally {
342
+ // Re-enable foreign key constraints
343
+ await connections.targetPool.query(
344
+ "SET session_replication_role = 'origin';"
345
+ );
346
+
347
+ // Close connections
348
+ await connections.sourcePool.end();
349
+ await connections.targetPool.end();
350
+ await connections.sourcePrisma.$disconnect();
351
+ await connections.targetPrisma.$disconnect();
352
+ }
353
+ }
354
+
355
+ // Step 6: Post-migration validation
356
+ if (!options.skipValidation) {
357
+ console.log("Step 6: Running post-migration validation...");
358
+ const { PostMigrationValidator } = await import(
359
+ "./post-migration-validator"
360
+ );
361
+ const validator = new PostMigrationValidator();
362
+ const result = await validator.validate({
363
+ publicOnly: options.mode === "public-only",
364
+ });
365
+
366
+ if (result.success) {
367
+ console.log("Post-migration validation successful!");
368
+ } else {
369
+ console.log(
370
+ `Post-migration validation found ${result.issueCount} issues.`
371
+ );
372
+ console.log(JSON.stringify(result, null, 2));
373
+
374
+ if (!options.force) {
375
+ console.error(
376
+ "Migration validation failed. Use --force to continue anyway."
377
+ );
378
+ process.exit(1);
379
+ }
380
+ console.log(
381
+ "Continuing with migration despite validation issues (--force flag provided)"
382
+ );
383
+ }
384
+ }
385
+
386
+ // Step 7: Synchronize sequences (final step)
387
+ if (!options.skipSequenceSync) {
388
+ console.log("Step 7: Synchronizing database sequences...");
389
+ const sequenceSynchronizer = new SequenceSynchronizer(
390
+ process.env.DATABASE_URL
391
+ );
392
+ await sequenceSynchronizer.synchronizeSequences();
393
+ console.log("Database sequences synchronized successfully!");
394
+ }
395
+
396
+ console.log("Migration process completed successfully!");
397
+ } catch (error) {
398
+ console.error("Error running migration:", error);
399
+ process.exit(1);
400
+ }
401
+ });
402
+
403
+ program
404
+ .command("fix-data-types")
405
+ .description("Fix data type issues in the migrated data")
406
+ .option("-t, --target <url>", "Target database connection URL")
407
+ .action(async (options) => {
408
+ if (options.target) {
409
+ process.env.DATABASE_URL = options.target;
410
+ }
411
+
412
+ if (!process.env.DATABASE_URL) {
413
+ console.error(
414
+ "Target database URL is required. Use --target or set DATABASE_URL environment variable."
415
+ );
416
+ process.exit(1);
417
+ }
418
+
419
+ try {
420
+ console.log("Fixing data type issues...");
421
+ const { DataTypeFixer } = await import("./fix-data-types");
422
+ const fixer = new DataTypeFixer();
423
+ await fixer.fixDataTypes();
424
+ console.log("Data type fixing completed successfully!");
425
+ } catch (error) {
426
+ console.error("Error fixing data types:", error);
427
+ process.exit(1);
428
+ }
429
+ });
430
+
431
+ program
432
+ .command("truncate-database")
433
+ .description(
434
+ "Truncate (empty) all tables in the database without dropping the database"
435
+ )
436
+ .option("-t, --target <url>", "Target database connection URL")
437
+ .action(async (options) => {
438
+ if (options.target) {
439
+ process.env.DATABASE_URL = options.target;
440
+ }
441
+
442
+ if (!process.env.DATABASE_URL) {
443
+ console.error(
444
+ "Target database URL is required. Use --target or set DATABASE_URL environment variable."
445
+ );
446
+ process.exit(1);
447
+ }
448
+
449
+ try {
450
+ console.log("Truncating database...");
451
+ const { DatabaseTruncateTool } = await import("./truncate-database");
452
+ const truncateTool = new DatabaseTruncateTool(process.env.DATABASE_URL);
453
+ await truncateTool.truncateDatabase();
454
+ console.log("Database truncation completed successfully!");
455
+ } catch (error) {
456
+ console.error("Error truncating database:", error);
457
+ process.exit(1);
458
+ }
459
+ });
460
+
461
+ program
462
+ .command("retry-failed")
463
+ .description("Retry failed migrations from an error log file")
464
+ .argument("<error-log-path>", "Path to the error log file")
465
+ .option("-t, --target <url>", "Target database connection URL")
466
+ .action(async (errorLogPath, options) => {
467
+ if (options.target) {
468
+ process.env.DATABASE_URL = options.target;
469
+ }
470
+
471
+ if (!process.env.DATABASE_URL) {
472
+ console.error(
473
+ "Target database URL is required. Use --target or set DATABASE_URL environment variable."
474
+ );
475
+ process.exit(1);
476
+ }
477
+
478
+ try {
479
+ console.log(`Retrying failed migrations from log: ${errorLogPath}`);
480
+ const { FailedMigrationRetry } = await import(
481
+ "./retry-failed-migrations"
482
+ );
483
+ const retryTool = new FailedMigrationRetry(errorLogPath);
484
+ await retryTool.retryFailedMigrations();
485
+ console.log("Retry process completed!");
486
+ } catch (error) {
487
+ console.error("Error retrying failed migrations:", error);
488
+ process.exit(1);
489
+ }
490
+ });
491
+
492
+ program
493
+ .command("fix-enum-values")
494
+ .description("Fix invalid enum values in the database")
495
+ .option("-t, --target <url>", "Target database connection URL")
496
+ .action(async (options) => {
497
+ if (options.target) {
498
+ process.env.DATABASE_URL = options.target;
499
+ }
500
+
501
+ if (!process.env.DATABASE_URL) {
502
+ console.error(
503
+ "Target database URL is required. Use --target or set DATABASE_URL environment variable."
504
+ );
505
+ process.exit(1);
506
+ }
507
+
508
+ try {
509
+ console.log("Fixing enum values...");
510
+ const { EnumFixer } = await import("./fix-enum-values");
511
+ const enumFixer = new EnumFixer(process.env.DATABASE_URL);
512
+ await enumFixer.fixEnumValues();
513
+ console.log("Enum values fixed successfully!");
514
+ } catch (error) {
515
+ console.error("Error fixing enum values:", error);
516
+ process.exit(1);
517
+ }
518
+ });
519
+
520
+ program
521
+ .command("pre-validate")
522
+ .description("Validate database before migration")
523
+ .option("-s, --source <url>", "Source database connection URL")
524
+ .option("-t, --target <url>", "Target database connection URL")
525
+ .option("-y, --yes", "Automatically answer yes to all prompts", false)
526
+ .action(async (options) => {
527
+ if (options.source) {
528
+ process.env.SOURCE_DATABASE_URL = options.source;
529
+ }
530
+ if (options.target) {
531
+ process.env.DATABASE_URL = options.target;
532
+ }
533
+
534
+ if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
535
+ console.error(
536
+ "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
537
+ );
538
+ process.exit(1);
539
+ }
540
+
541
+ try {
542
+ console.log("Validating database before migration...");
543
+ const { PreMigrationValidator } = await import(
544
+ "./pre-migration-validator"
545
+ );
546
+ const validator = new PreMigrationValidator();
547
+ const result = await validator.validate();
548
+
549
+ if (result.success) {
550
+ console.log("Pre-migration validation successful!");
551
+ process.exit(0);
552
+ } else {
553
+ console.log(
554
+ `Pre-migration validation found ${result.issueCount} issues.`
555
+ );
556
+ console.log(JSON.stringify(result, null, 2));
557
+
558
+ // Si la opción --yes está activada, continuar automáticamente
559
+ if (options.yes) {
560
+ console.log(
561
+ "Continuing with migration despite issues (--yes flag provided)"
562
+ );
563
+ process.exit(0);
564
+ }
565
+
566
+ // Arreglar el problema con readline usando una promesa
567
+ const readline = require("readline").createInterface({
568
+ input: process.stdin,
569
+ output: process.stdout,
570
+ });
571
+
572
+ const answer = await new Promise<string>((resolve) => {
573
+ readline.question(
574
+ "Continue with migration despite issues? (y/N) ",
575
+ (ans: string) => {
576
+ resolve(ans);
577
+ readline.close();
578
+ }
579
+ );
580
+ });
581
+
582
+ if (answer.toLowerCase() === "y") {
583
+ console.log("Continuing with migration despite issues.");
584
+ process.exit(0);
585
+ } else {
586
+ console.log("Migration aborted by user.");
587
+ process.exit(1);
588
+ }
589
+ }
590
+ } catch (error) {
591
+ console.error("Error during pre-migration validation:", error);
592
+ process.exit(1);
593
+ }
594
+ });
595
+
596
+ program
597
+ .command("sync-enums")
598
+ .description("Synchronize enum types between databases")
599
+ .option("-s, --source <url>", "Source database connection URL")
600
+ .option("-t, --target <url>", "Target database connection URL")
601
+ .action(async (options) => {
602
+ if (options.source) {
603
+ process.env.SOURCE_DATABASE_URL = options.source;
604
+ }
605
+ if (options.target) {
606
+ process.env.DATABASE_URL = options.target;
607
+ }
608
+
609
+ if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
610
+ console.error(
611
+ "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
612
+ );
613
+ process.exit(1);
614
+ }
615
+
616
+ try {
617
+ console.log("Synchronizing enum types...");
618
+ const { EnumSynchronizer } = await import("./sync-enum-types");
619
+ const synchronizer = new EnumSynchronizer();
620
+ await synchronizer.synchronizeEnums();
621
+ console.log("Enum types synchronized successfully!");
622
+ } catch (error) {
623
+ console.error("Error synchronizing enum types:", error);
624
+ process.exit(1);
625
+ }
626
+ });
627
+
628
+ program
629
+ .command("post-validate")
630
+ .description("Validate database after migration")
631
+ .option("-s, --source <url>", "Source database connection URL")
632
+ .option("-t, --target <url>", "Target database connection URL")
633
+ .action(async (options) => {
634
+ if (options.source) {
635
+ process.env.SOURCE_DATABASE_URL = options.source;
636
+ }
637
+ if (options.target) {
638
+ process.env.DATABASE_URL = options.target;
639
+ }
640
+
641
+ if (!process.env.SOURCE_DATABASE_URL || !process.env.DATABASE_URL) {
642
+ console.error(
643
+ "Source and target database URLs are required. Use --source and --target or set SOURCE_DATABASE_URL and DATABASE_URL environment variables."
644
+ );
645
+ process.exit(1);
646
+ }
647
+
648
+ try {
649
+ console.log("Validating database after migration...");
650
+ const { PostMigrationValidator } = await import(
651
+ "./post-migration-validator"
652
+ );
653
+ const validator = new PostMigrationValidator();
654
+ const result = await validator.validate();
655
+
656
+ if (result.success) {
657
+ console.log("Post-migration validation successful!");
658
+ } else {
659
+ console.log(
660
+ `Post-migration validation found ${result.issueCount} issues.`
661
+ );
662
+ console.log(JSON.stringify(result, null, 2));
663
+ }
664
+ } catch (error) {
665
+ console.error("Error during post-migration validation:", error);
666
+ process.exit(1);
667
+ }
668
+ });
669
+
670
+ program
671
+ .command("verify")
672
+ .description("Verify migration setup and connections")
673
+ .action(async () => {
674
+ try {
675
+ const { MigrationSetupVerifier } = await import(
676
+ "./verify-migration-setup"
677
+ );
678
+ const verifier = new MigrationSetupVerifier();
679
+ await verifier.verifySetup();
680
+ } catch (error) {
681
+ console.error("Verification failed:", error);
682
+ process.exit(1);
683
+ }
684
+ });
685
+
686
+ program
687
+ .command("fix-schema")
688
+ .description("Fix schema discrepancies between source and target databases")
689
+ .option("-r, --report <path>", "Path to pre-migration report file")
690
+ .action(async (options) => {
691
+ try {
692
+ const fixer = new SchemaDiscrepancyFixer(
693
+ process.env.SOURCE_DATABASE_URL,
694
+ process.env.DATABASE_URL,
695
+ options.report
696
+ );
697
+
698
+ await fixer.fixDiscrepancies();
699
+ console.log("Schema discrepancies fixed successfully");
700
+ } catch (error) {
701
+ console.error("Error fixing schema discrepancies:", error.message);
702
+ process.exit(1);
703
+ }
704
+ });
705
+
706
+ // Remove the unused run() function or call it if needed
707
+ program.parse();