From ee15e034b5168a2e078452fd6ad72ae5d51d6d3a Mon Sep 17 00:00:00 2001 From: Etienne <45695613+etiennejouan@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:09:52 +0100 Subject: [PATCH] Files command - fixes (#18016) - Fixed "property entity not found" error when updating/creating a new field and querying the same object repository just after - Downgraded log type for unnecessary migration --- ...y-rich-text-attachment-file-ids.command.ts | 2 +- .../1-18-migrate-attachment-files.command.ts | 187 +++++++++--------- ...-18-migrate-person-avatar-files.command.ts | 174 ++++++++-------- .../1-18-upgrade-version-command.module.ts | 2 +- 4 files changed, 188 insertions(+), 177 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command.ts index 5951e93075..15d6ebbaf7 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-activity-rich-text-attachment-file-ids.command.ts @@ -444,7 +444,7 @@ export class MigrateActivityRichTextAttachmentFileIdsCommand extends ActiveOrSus return fileId; } catch (error) { - this.logger.error( + this.logger.warn( `Failed to create attachment from URL ${url}: ${error.message}`, ); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command.ts index 754d4df4e1..3e4e562acb 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-attachment-files.command.ts @@ -25,6 +25,7 @@ import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity'; @@ -195,102 +196,108 @@ export class MigrateAttachmentFilesCommand extends ActiveOrSuspendedWorkspacesMi return; } - const attachmentRepository = - await this.twentyORMGlobalManager.getRepository( - workspaceId, - 'attachment', - { shouldBypassPermissionChecks: true }, + const systemAuthContext = buildSystemAuthContext(workspaceId); + + await this.twentyORMGlobalManager.executeInWorkspaceContext(async () => { + const attachmentRepository = + await this.twentyORMGlobalManager.getRepository( + workspaceId, + 'attachment', + { shouldBypassPermissionChecks: true }, + ); + + const attachments = await attachmentRepository.find({ + where: { + fullPath: Not(IsNull()), + file: Or(IsNull(), Equal([])), + }, + select: ['id', 'name', 'fullPath'], + }); + + if (attachments.length === 0) { + this.logger.log( + `No attachments to migrate for workspace ${workspaceId}`, + ); + + return; + } + + this.logger.log( + `Found ${attachments.length} attachment(s) to migrate in workspace ${workspaceId}`, ); - const attachments = await attachmentRepository.find({ - where: { - fullPath: Not(IsNull()), - file: Or(IsNull(), Equal([])), - }, - select: ['id', 'name', 'fullPath'], - }); + const fileRepository = this.coreDataSource.getRepository(FileEntity); - if (attachments.length === 0) { - this.logger.log(`No attachments to migrate for workspace ${workspaceId}`); - - return; - } - - this.logger.log( - `Found ${attachments.length} attachment(s) to migrate in workspace ${workspaceId}`, - ); - - const fileRepository = this.coreDataSource.getRepository(FileEntity); - - for (const attachment of attachments) { - if (!isNonEmptyString(attachment.fullPath)) { - this.logger.warn( - `Skipping attachment ${attachment.id} - invalid fullPath`, - ); - - continue; - } - - try { - const { type: fileExtension, filename } = - extractFolderPathFilenameAndTypeOrThrow(attachment.fullPath); - - const fileId = v4(); - const newFilename = `${fileId}${isNonEmptyString(fileExtension) ? `.${fileExtension}` : ''}`; - const newResourcePath = `${FileFolder.FilesField}/${attachmentFileflatFieldMetadata.universalIdentifier}/${newFilename}`; - - if (!isDryRun) { - await this.fileStorageService.copyLegacy({ - from: { - folderPath: `workspace-${workspaceId}`, - filename: attachment.fullPath, - }, - to: { - folderPath: `${workspaceId}/${twentyStandardFlatApplication.universalIdentifier}`, - filename: newResourcePath, - }, - }); - - const fileEntity = fileRepository.create({ - id: fileId, - path: newResourcePath, - workspaceId, - applicationId: twentyStandardFlatApplication.id, - size: -1, - settings: { - isTemporaryFile: true, - toDelete: false, - }, - }); - - await fileRepository.save(fileEntity); - - await attachmentRepository.update( - { id: attachment.id }, - { - file: [ - { - fileId: fileEntity.id, - label: attachment.name || filename, - }, - ], - }, + for (const attachment of attachments) { + if (!isNonEmptyString(attachment.fullPath)) { + this.logger.warn( + `Skipping attachment ${attachment.id} - invalid fullPath`, ); + + continue; } - this.logger.log( - `Migrated attachment ${attachment.id} (${attachment.name})`, - ); - } catch (error) { - this.logger.error( - `Failed to migrate attachment ${attachment.id} in workspace ${workspaceId}: ${error.message}`, - ); - throw error; - } - } + try { + const { type: fileExtension, filename } = + extractFolderPathFilenameAndTypeOrThrow(attachment.fullPath); - this.logger.log( - `${isDryRun ? '[DRY RUN] ' : ''}Completed attachment files migration for workspace ${workspaceId}`, - ); + const fileId = v4(); + const newFilename = `${fileId}${isNonEmptyString(fileExtension) ? `.${fileExtension}` : ''}`; + const newResourcePath = `${FileFolder.FilesField}/${attachmentFileflatFieldMetadata.universalIdentifier}/${newFilename}`; + + if (!isDryRun) { + await this.fileStorageService.copyLegacy({ + from: { + folderPath: `workspace-${workspaceId}`, + filename: attachment.fullPath, + }, + to: { + folderPath: `${workspaceId}/${twentyStandardFlatApplication.universalIdentifier}`, + filename: newResourcePath, + }, + }); + + const fileEntity = fileRepository.create({ + id: fileId, + path: newResourcePath, + workspaceId, + applicationId: twentyStandardFlatApplication.id, + size: -1, + settings: { + isTemporaryFile: true, + toDelete: false, + }, + }); + + await fileRepository.save(fileEntity); + + await attachmentRepository.update( + { id: attachment.id }, + { + file: [ + { + fileId: fileEntity.id, + label: attachment.name || filename, + }, + ], + }, + ); + } + + this.logger.log( + `Migrated attachment ${attachment.id} (${attachment.name})`, + ); + } catch (error) { + this.logger.error( + `Failed to migrate attachment ${attachment.id} in workspace ${workspaceId}: ${error.message}`, + ); + throw error; + } + } + + this.logger.log( + `${isDryRun ? '[DRY RUN] ' : ''}Completed attachment files migration for workspace ${workspaceId}`, + ); + }, systemAuthContext); } } diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command.ts index e811ba37de..a395eb97a8 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-migrate-person-avatar-files.command.ts @@ -26,6 +26,7 @@ import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity'; @@ -205,97 +206,100 @@ export class MigratePersonAvatarFilesCommand extends ActiveOrSuspendedWorkspaces return; } - const personRepository = - await this.twentyORMGlobalManager.getRepository( - workspaceId, - 'person', - { shouldBypassPermissionChecks: true }, - ); + const systemAuthContext = buildSystemAuthContext(workspaceId); - const personsWithAvatars = await personRepository.find({ - where: { - avatarUrl: ILike(`%${FileFolder.PersonPicture}%`), - avatarFile: Or(IsNull(), Equal([])), - }, - select: ['id', 'avatarUrl'], - }); - - if (personsWithAvatars.length === 0) { - this.logger.log( - `No persons with avatarUrl found in workspace ${workspaceId}`, - ); - - return; - } - - this.logger.log( - `Found ${personsWithAvatars.length} person(s) with avatarUrl containing 'people' folder in workspace ${workspaceId}`, - ); - - const fileRepository = this.coreDataSource.getRepository(FileEntity); - - for (const person of personsWithAvatars) { - assertIsDefinedOrThrow(person.avatarUrl); - - try { - const { type: fileExtension } = extractFolderPathFilenameAndTypeOrThrow( - person.avatarUrl, + await this.twentyORMGlobalManager.executeInWorkspaceContext(async () => { + const personRepository = + await this.twentyORMGlobalManager.getRepository( + workspaceId, + 'person', + { shouldBypassPermissionChecks: true }, ); - const fileId = v4(); - const newFileName = `${fileId}${isNonEmptyString(fileExtension) ? `.${fileExtension}` : ''}`; - const newResourcePath = `${FileFolder.FilesField}/${avatarFileFieldMetadata.universalIdentifier}/${newFileName}`; + const personsWithAvatars = await personRepository.find({ + where: { + avatarUrl: ILike(`%${FileFolder.PersonPicture}%`), + avatarFile: Or(IsNull(), Equal([])), + }, + select: ['id', 'avatarUrl'], + }); - if (!isDryRun) { - await this.fileStorageService.copyLegacy({ - from: { - folderPath: `workspace-${workspaceId}`, - filename: person.avatarUrl, - }, - to: { - folderPath: `${workspaceId}/${twentyStandardFlatApplication.universalIdentifier}`, - filename: newResourcePath, - }, - }); - - const fileEntity = fileRepository.create({ - id: fileId, - path: newResourcePath, - workspaceId, - applicationId: twentyStandardFlatApplication.id, - size: -1, - settings: { - isTemporaryFile: true, - toDelete: false, - }, - }); - - await fileRepository.save(fileEntity); - - await personRepository.update( - { id: person.id }, - { - avatarFile: [ - { - fileId: fileEntity.id, - label: newFileName, - }, - ], - }, - ); - } - - this.logger.log(`Migrated avatar for person ${person.id}`); - } catch (error) { - this.logger.error( - `Failed to migrate avatar for person ${person.id} in workspace ${workspaceId}: ${error.message}`, + if (personsWithAvatars.length === 0) { + this.logger.log( + `No persons with avatarUrl found in workspace ${workspaceId}`, ); - throw error; + + return; } - } - this.logger.log( - `${isDryRun ? '[DRY RUN] ' : ''}Completed person avatar files migration for workspace ${workspaceId}`, - ); + this.logger.log( + `Found ${personsWithAvatars.length} person(s) with avatarUrl containing 'people' folder in workspace ${workspaceId}`, + ); + + const fileRepository = this.coreDataSource.getRepository(FileEntity); + + for (const person of personsWithAvatars) { + assertIsDefinedOrThrow(person.avatarUrl); + + try { + const { type: fileExtension } = + extractFolderPathFilenameAndTypeOrThrow(person.avatarUrl); + + const fileId = v4(); + const newFileName = `${fileId}${isNonEmptyString(fileExtension) ? `.${fileExtension}` : ''}`; + const newResourcePath = `${FileFolder.FilesField}/${avatarFileFieldMetadata.universalIdentifier}/${newFileName}`; + + if (!isDryRun) { + await this.fileStorageService.copyLegacy({ + from: { + folderPath: `workspace-${workspaceId}`, + filename: person.avatarUrl, + }, + to: { + folderPath: `${workspaceId}/${twentyStandardFlatApplication.universalIdentifier}`, + filename: newResourcePath, + }, + }); + + const fileEntity = fileRepository.create({ + id: fileId, + path: newResourcePath, + workspaceId, + applicationId: twentyStandardFlatApplication.id, + size: -1, + settings: { + isTemporaryFile: true, + toDelete: false, + }, + }); + + await fileRepository.save(fileEntity); + + await personRepository.update( + { id: person.id }, + { + avatarFile: [ + { + fileId: fileEntity.id, + label: newFileName, + }, + ], + }, + ); + } + + this.logger.log(`Migrated avatar for person ${person.id}`); + } catch (error) { + this.logger.error( + `Failed to migrate avatar for person ${person.id} in workspace ${workspaceId}: ${error.message}`, + ); + throw error; + } + } + + this.logger.log( + `${isDryRun ? '[DRY RUN] ' : ''}Completed person avatar files migration for workspace ${workspaceId}`, + ); + }, systemAuthContext); } } diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts index 8b0418a13a..a0c5f7cc8c 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-18/1-18-upgrade-version-command.module.ts @@ -71,8 +71,8 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta BackfillMessageChannelThrottleRetryAfterCommand, BackfillStandardViewsAndFieldMetadataCommand, MigrateWorkspacePicturesCommand, - BackfillFileSizeAndMimeTypeCommand, MigrateWorkflowSendEmailAttachmentsCommand, + BackfillFileSizeAndMimeTypeCommand, ], }) export class V1_18_UpgradeVersionCommandModule {}