Fixes - Workspace logo migration (#18035)

- Update migration command to handle case where workspace logo is
originated from workspace email and point to twenty-icons.com
- Update same logic for new workspaces
- Add feature-flag for all newly created workspaces
This commit is contained in:
Etienne
2026-02-18 16:35:48 +01:00
committed by GitHub
parent 3bd431e95d
commit 058489b5cc
11 changed files with 381 additions and 125 deletions
@@ -16,11 +16,15 @@ import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.ent
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
@UseGuards(WorkspaceAuthGuard)
@UsePipes(ResolverValidationPipe)
@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter)
@UseFilters(
PermissionsGraphqlApiExceptionFilter,
PreventNestToAutoLogGraphqlErrorsFilter,
)
@MetadataResolver()
export class FileCorePictureResolver {
constructor(
@@ -64,7 +64,7 @@ export class FileCorePictureService {
const savedFile = await this.fileStorageService.writeFile({
sourceFile: sanitizedFile,
resourcePath: `${FileFolder.CorePicture}/${finalName}`,
resourcePath: finalName,
mimeType,
fileFolder: FileFolder.CorePicture,
applicationUniversalIdentifier: universalIdentifier,
@@ -182,6 +182,25 @@ export class FileCorePictureService {
});
}
private async fetchImageBufferFromUrl(
imageUrl: string,
): Promise<{ buffer: Buffer; extension: string } | undefined> {
try {
const httpClient = this.secureHttpClientService.getHttpClient();
const buffer = await getImageBufferFromUrl(imageUrl, httpClient);
const type = await FileType.fromBuffer(buffer);
if (!isDefined(type) || !type.mime.startsWith('image/')) {
return undefined;
}
return { buffer, extension: type.ext };
} catch {
return undefined;
}
}
async uploadWorkspaceMemberProfilePictureFromUrl({
imageUrl,
workspaceId,
@@ -193,18 +212,41 @@ export class FileCorePictureService {
applicationUniversalIdentifier?: string;
queryRunner?: QueryRunner;
}): Promise<FileWithSignedUrlDto | undefined> {
const httpClient = this.secureHttpClientService.getHttpClient();
const buffer = await getImageBufferFromUrl(imageUrl, httpClient);
const imageData = await this.fetchImageBufferFromUrl(imageUrl);
const type = await FileType.fromBuffer(buffer);
if (!isDefined(type) || !type.mime.startsWith('image/')) {
return;
if (!isDefined(imageData)) {
return undefined;
}
return this.uploadWorkspaceMemberProfilePicture({
file: buffer,
filename: `avatar.${type.ext}`,
file: imageData.buffer,
filename: `avatar.${imageData.extension}`,
workspaceId,
applicationUniversalIdentifier,
queryRunner,
});
}
async uploadWorkspaceLogoFromUrl({
imageUrl,
workspaceId,
applicationUniversalIdentifier,
queryRunner,
}: {
imageUrl: string;
workspaceId: string;
applicationUniversalIdentifier?: string;
queryRunner?: QueryRunner;
}): Promise<FileEntity | undefined> {
const imageData = await this.fetchImageBufferFromUrl(imageUrl);
if (!isDefined(imageData)) {
return undefined;
}
return this.uploadCorePicture({
file: imageData.buffer,
filename: `logo.${imageData.extension}`,
workspaceId,
applicationUniversalIdentifier,
queryRunner,