import { pgTable, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core'; /** * Idempotency ledger for one-shot data migrations from external sources * (e.g. the legacy NocoDB Interests table). * * Every entity created during a migration script's `--apply` run gets a * row here mapping the source-system row identifier to the new-system * entity id. Re-running `--apply` against the same report skips rows * already linked, so partial-failure resumption is just "run again." * * One source row can generate multiple new entities (e.g. one NocoDB * Interests row → one client + one interest + one yacht), so the * uniqueness constraint includes `target_entity_type`. */ export const migrationSourceLinks = pgTable( 'migration_source_links', { id: text('id') .primaryKey() .$defaultFn(() => crypto.randomUUID()), /** e.g. 'nocodb_interests', 'nocodb_residences', 'nocodb_website_submissions'. */ sourceSystem: text('source_system').notNull(), /** Source row identifier as a string (NocoDB IDs are integers; we keep * text here for forward compat with other sources). */ sourceId: text('source_id').notNull(), /** e.g. 'client', 'interest', 'yacht', 'document'. */ targetEntityType: text('target_entity_type').notNull(), /** UUID of the new-system entity (clients.id, interests.id, etc.). */ targetEntityId: text('target_entity_id').notNull(), /** Apply-id from the migration run that created this link - pairs with * the on-disk apply manifest so `--rollback --apply-id ` knows * exactly which links to remove. */ appliedId: text('applied_id').notNull(), appliedBy: text('applied_by'), appliedAt: timestamp('applied_at', { withTimezone: true }).notNull().defaultNow(), }, (table) => [ uniqueIndex('idx_msl_source_target').on( table.sourceSystem, table.sourceId, table.targetEntityType, ), ], ); export type MigrationSourceLink = typeof migrationSourceLinks.$inferSelect; export type NewMigrationSourceLink = typeof migrationSourceLinks.$inferInsert;