diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts index 8fc34e5a29..3346b29d74 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts @@ -40,6 +40,7 @@ export class DeleteFileRecordsCommand extends ActiveOrSuspendedWorkspacesMigrati ); const files = await this.fileRepository.find({ + select: ['id'], where: { workspaceId }, withDeleted: true, }); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-update-file-table-migration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-update-file-table-migration.command.ts new file mode 100644 index 0000000000..9b7e9ba601 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-update-file-table-migration.command.ts @@ -0,0 +1,66 @@ +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { DataSource, Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { updateFileTableQueries } from 'src/database/typeorm/core/migrations/utils/1768572831179-updateFileTable.util'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; + +@Command({ + name: 'upgrade:1-17:update-file-table-migration', + description: 'Update file table schema with applicationId and new columns', +}) +export class UpdateFileTableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + private hasRunOnce = false; + + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + @InjectDataSource() + private readonly coreDataSource: DataSource, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + options, + }: RunOnWorkspaceArgs): Promise { + if (this.hasRunOnce) { + this.logger.warn( + 'Skipping has already been run once UpdateFileTableMigrationCommand', + ); + + return; + } + + if (options.dryRun) { + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await updateFileTableQueries(queryRunner); + + await queryRunner.commitTransaction(); + this.logger.log('Successfully run UpdateFileTableMigrationCommand'); + this.hasRunOnce = true; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error( + `Rolling back UpdateFileTableMigrationCommand: ${error.message}`, + ); + } finally { + await queryRunner.release(); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts index 5b28158b3c..0ac2742042 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts @@ -11,6 +11,7 @@ import { MigrateSendEmailRecipientsCommand } from 'src/database/commands/upgrade import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command'; import { MigrateWorkflowCodeStepsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-workflow-code-steps.command'; import { SeedWorkflowV1_16Command } from 'src/database/commands/upgrade-version-command/1-17/1-17-seed-workflow-v1-16.command'; +import { UpdateFileTableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-update-file-table-migration.command'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; @@ -72,6 +73,7 @@ import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/tas MigrateWorkflowCodeStepsCommand, SeedWorkflowV1_16Command, BackfillApplicationPackageFilesCommand, + UpdateFileTableMigrationCommand, ], exports: [ MigrateAttachmentToMorphRelationsCommand, @@ -84,6 +86,7 @@ import { TaskTargetWorkspaceEntity } from 'src/modules/task/standard-objects/tas MigrateWorkflowCodeStepsCommand, SeedWorkflowV1_16Command, BackfillApplicationPackageFilesCommand, + UpdateFileTableMigrationCommand, ], }) export class V1_17_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index 269a36ba04..814b1fdc41 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -17,6 +17,7 @@ import { MigrateAttachmentToMorphRelationsCommand } from 'src/database/commands/ import { MigrateNoteTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-note-target-to-morph-relations.command'; import { MigrateTaskTargetToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-task-target-to-morph-relations.command'; import { MigrateWorkflowCodeStepsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-workflow-code-steps.command'; +import { UpdateFileTableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-update-file-table-migration.command'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; @@ -45,6 +46,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly identifyWebhookMetadataCommand: IdentifyWebhookMetadataCommand, protected readonly makeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly migrateWorkflowCodeStepsCommand: MigrateWorkflowCodeStepsCommand, + protected readonly updateFileTableMigrationCommand: UpdateFileTableMigrationCommand, ) { super( workspaceRepository, @@ -66,6 +68,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.migrateWorkflowCodeStepsCommand, this.deleteFileRecordsCommand, this.backfillApplicationPackageFilesCommand, + this.updateFileTableMigrationCommand, ]; this.allCommands = { diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1768572831179-updateFileTable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768572831179-updateFileTable.ts index 3479786ef1..771bb67a2d 100644 --- a/packages/twenty-server/src/database/typeorm/core/migrations/common/1768572831179-updateFileTable.ts +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768572831179-updateFileTable.ts @@ -1,30 +1,35 @@ import { type MigrationInterface, type QueryRunner } from 'typeorm'; +import { updateFileTableQueries } from 'src/database/typeorm/core/migrations/utils/1768572831179-updateFileTable.util'; + export class UpdateFileTable1768572831179 implements MigrationInterface { name = 'UpdateFileTable1768572831179'; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "fullPath"`); - await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "type"`); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD "applicationId" uuid`, - ); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD "path" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, - ); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`, - ); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD "isStaticAsset" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "core"."file" ADD CONSTRAINT "FK_413aaaf293284c3c0266d0bab3a" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`, - ); + const savepointName = 'sp_update_file_table'; + + try { + await queryRunner.query(`SAVEPOINT ${savepointName}`); + + await updateFileTableQueries(queryRunner); + + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (e) { + try { + await queryRunner.query(`ROLLBACK TO SAVEPOINT ${savepointName}`); + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (rollbackError) { + // eslint-disable-next-line no-console + console.error( + 'Failed to rollback to savepoint in UpdateFileTable1768572831179', + rollbackError, + ); + throw rollbackError; + } + + // eslint-disable-next-line no-console + console.error('Swallowing UpdateFileTable1768572831179 error', e); + } } public async down(queryRunner: QueryRunner): Promise { diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768572831179-updateFileTable.util.ts b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768572831179-updateFileTable.util.ts new file mode 100644 index 0000000000..92f22c5f17 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768572831179-updateFileTable.util.ts @@ -0,0 +1,25 @@ +import { type QueryRunner } from 'typeorm'; + +export const updateFileTableQueries = async ( + queryRunner: QueryRunner, +): Promise => { + await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "name"`); + await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "fullPath"`); + await queryRunner.query(`ALTER TABLE "core"."file" DROP COLUMN "type"`); + await queryRunner.query(`ALTER TABLE "core"."file" ADD "applicationId" uuid`); + await queryRunner.query( + `ALTER TABLE "core"."file" ADD "path" character varying NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."file" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, + ); + await queryRunner.query( + `ALTER TABLE "core"."file" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `ALTER TABLE "core"."file" ADD "isStaticAsset" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "core"."file" ADD CONSTRAINT "FK_413aaaf293284c3c0266d0bab3a" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`, + ); +};