From 1e9a1bacc9181c4873406ad1634db7ab32e35ecf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 06:45:21 +0000 Subject: [PATCH 1/2] Initial plan From 9a0448a6ecf4c1684500e0e9d83ec377e5029276 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 06:47:37 +0000 Subject: [PATCH 2/2] fix: apply EXIF orientation during thumbnail generation with sharp .rotate() Add .rotate() before .resize() in all thumbnail generation paths so that EXIF Orientation metadata is respected. This fixes rotated thumbnails for smartphone photos in Gallery View and attachment previews. Closes #2854 --- .../features/attachments/attachments-storage.service.ts | 2 +- .../src/features/attachments/plugins/local.ts | 2 +- .../src/features/attachments/plugins/minio.ts | 7 +++---- apps/nestjs-backend/src/features/attachments/plugins/s3.ts | 7 +++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/nestjs-backend/src/features/attachments/attachments-storage.service.ts b/apps/nestjs-backend/src/features/attachments/attachments-storage.service.ts index d445a6953a..9f055b26cb 100644 --- a/apps/nestjs-backend/src/features/attachments/attachments-storage.service.ts +++ b/apps/nestjs-backend/src/features/attachments/attachments-storage.service.ts @@ -127,7 +127,7 @@ export class AttachmentsStorageService { ) { const mimetype = ATTACHMENT_THUMBNAIL_DEFAULT_MIMETYPE; const { smThumbnailPath, lgThumbnailPath } = generateTableThumbnailPath(path); - const image = sharp(imageBuffer, { failOn: 'none', unlimited: true }); + const image = sharp(imageBuffer, { failOn: 'none', unlimited: true }).rotate(); let cutSmThumbnailPath: string | undefined; let cutLgThumbnailPath: string | undefined; diff --git a/apps/nestjs-backend/src/features/attachments/plugins/local.ts b/apps/nestjs-backend/src/features/attachments/plugins/local.ts index 3d609578db..f952fda806 100644 --- a/apps/nestjs-backend/src/features/attachments/plugins/local.ts +++ b/apps/nestjs-backend/src/features/attachments/plugins/local.ts @@ -365,7 +365,7 @@ export class LocalStorage implements StorageAdapter { }, }); } - const resizedImage = image.resize(width, height); + const resizedImage = image.rotate().resize(width, height); await resizedImage.toFile(resizedImagePath); return newPath; } diff --git a/apps/nestjs-backend/src/features/attachments/plugins/minio.ts b/apps/nestjs-backend/src/features/attachments/plugins/minio.ts index 699b675079..2258ab3147 100644 --- a/apps/nestjs-backend/src/features/attachments/plugins/minio.ts +++ b/apps/nestjs-backend/src/features/attachments/plugins/minio.ts @@ -253,10 +253,9 @@ export class MinioStorage implements StorageAdapter { writeStream.removeAllListeners(); writeStream.destroy(); } - const metaReader = sharp(sourceFilePath, { failOn: 'none', unlimited: true }).resize( - width, - height - ); + const metaReader = sharp(sourceFilePath, { failOn: 'none', unlimited: true }) + .rotate() + .resize(width, height); await metaReader.toFile(resizedImagePath); // delete source file fse.removeSync(sourceFilePath); diff --git a/apps/nestjs-backend/src/features/attachments/plugins/s3.ts b/apps/nestjs-backend/src/features/attachments/plugins/s3.ts index 7340a2de68..9603729c6a 100644 --- a/apps/nestjs-backend/src/features/attachments/plugins/s3.ts +++ b/apps/nestjs-backend/src/features/attachments/plugins/s3.ts @@ -456,10 +456,9 @@ export class S3Storage implements StorageAdapter { writeStream.on('error', reject); (stream as Readable).on('error', reject); }); - const metaReader = sharp(sourceFilePath, { failOn: 'none', unlimited: true }).resize( - width, - height - ); + const metaReader = sharp(sourceFilePath, { failOn: 'none', unlimited: true }) + .rotate() + .resize(width, height); await metaReader.toFile(resizedImagePath); fse.removeSync(sourceFilePath); const upload = await this.uploadFileWidthPath(bucket, newPath, resizedImagePath, {