fix: iOS portrait photo saved as jpg extension (#21388)

remove bad merged settings

remove console log
This commit is contained in:
Alex 2025-09-02 14:26:12 -05:00 committed by GitHub
parent 28edf5664d
commit 4f7702c6bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 36 additions and 21 deletions

View file

@ -12,7 +12,7 @@ import { AuthRequest } from 'src/middleware/auth.guard';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { AssetMediaService } from 'src/services/asset-media.service'; import { AssetMediaService } from 'src/services/asset-media.service';
import { ImmichFile, UploadFile, UploadFiles } from 'src/types'; import { ImmichFile, UploadFile, UploadFiles } from 'src/types';
import { asRequest, mapToUploadFile } from 'src/utils/asset.util'; import { asUploadRequest, mapToUploadFile } from 'src/utils/asset.util';
export function getFile(files: UploadFiles, property: 'assetData' | 'sidecarData') { export function getFile(files: UploadFiles, property: 'assetData' | 'sidecarData') {
const file = files[property]?.[0]; const file = files[property]?.[0];
@ -99,18 +99,21 @@ export class FileUploadInterceptor implements NestInterceptor {
} }
private fileFilter(request: AuthRequest, file: Express.Multer.File, callback: multer.FileFilterCallback) { private fileFilter(request: AuthRequest, file: Express.Multer.File, callback: multer.FileFilterCallback) {
return callbackify(() => this.assetService.canUploadFile(asRequest(request, file)), callback); return callbackify(() => this.assetService.canUploadFile(asUploadRequest(request, file)), callback);
} }
private filename(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) { private filename(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) {
return callbackify( return callbackify(
() => this.assetService.getUploadFilename(asRequest(request, file)), () => this.assetService.getUploadFilename(asUploadRequest(request, file)),
callback as Callback<string>, callback as Callback<string>,
); );
} }
private destination(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) { private destination(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) {
return callbackify(() => this.assetService.getUploadFolder(asRequest(request, file)), callback as Callback<string>); return callbackify(
() => this.assetService.getUploadFolder(asUploadRequest(request, file)),
callback as Callback<string>,
);
} }
private handleFile(request: AuthRequest, file: Express.Multer.File, callback: Callback<Partial<ImmichFile>>) { private handleFile(request: AuthRequest, file: Express.Multer.File, callback: Callback<Partial<ImmichFile>>) {

View file

@ -25,6 +25,7 @@ const file1 = Buffer.from('d2947b871a706081be194569951b7db246907957', 'hex');
const uploadFile = { const uploadFile = {
nullAuth: { nullAuth: {
auth: null, auth: null,
body: {},
fieldName: UploadFieldName.ASSET_DATA, fieldName: UploadFieldName.ASSET_DATA,
file: { file: {
uuid: 'random-uuid', uuid: 'random-uuid',
@ -37,6 +38,7 @@ const uploadFile = {
filename: (fieldName: UploadFieldName, filename: string) => { filename: (fieldName: UploadFieldName, filename: string) => {
return { return {
auth: authStub.admin, auth: authStub.admin,
body: {},
fieldName, fieldName,
file: { file: {
uuid: 'random-uuid', uuid: 'random-uuid',
@ -897,7 +899,10 @@ describe(AssetMediaService.name, () => {
describe('onUploadError', () => { describe('onUploadError', () => {
it('should queue a job to delete the uploaded file', async () => { it('should queue a job to delete the uploaded file', async () => {
const request = { user: authStub.user1 } as AuthRequest; const request = {
body: {},
user: authStub.user1,
} as AuthRequest;
const file = { const file = {
fieldname: UploadFieldName.ASSET_DATA, fieldname: UploadFieldName.ASSET_DATA,

View file

@ -24,20 +24,14 @@ import { AuthDto } from 'src/dtos/auth.dto';
import { AssetStatus, AssetType, AssetVisibility, CacheControl, JobName, Permission, StorageFolder } from 'src/enum'; import { AssetStatus, AssetType, AssetVisibility, CacheControl, JobName, Permission, StorageFolder } from 'src/enum';
import { AuthRequest } from 'src/middleware/auth.guard'; import { AuthRequest } from 'src/middleware/auth.guard';
import { BaseService } from 'src/services/base.service'; import { BaseService } from 'src/services/base.service';
import { UploadFile } from 'src/types'; import { UploadFile, UploadRequest } from 'src/types';
import { requireUploadAccess } from 'src/utils/access'; import { requireUploadAccess } from 'src/utils/access';
import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util'; import { asUploadRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
import { isAssetChecksumConstraint } from 'src/utils/database'; import { isAssetChecksumConstraint } from 'src/utils/database';
import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file'; import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file';
import { mimeTypes } from 'src/utils/mime-types'; import { mimeTypes } from 'src/utils/mime-types';
import { fromChecksum } from 'src/utils/request'; import { fromChecksum } from 'src/utils/request';
interface UploadRequest {
auth: AuthDto | null;
fieldName: UploadFieldName;
file: UploadFile;
}
export interface AssetMediaRedirectResponse { export interface AssetMediaRedirectResponse {
targetSize: AssetMediaSize | 'original'; targetSize: AssetMediaSize | 'original';
} }
@ -89,15 +83,15 @@ export class AssetMediaService extends BaseService {
throw new BadRequestException(`Unsupported file type ${filename}`); throw new BadRequestException(`Unsupported file type ${filename}`);
} }
getUploadFilename({ auth, fieldName, file }: UploadRequest): string { getUploadFilename({ auth, fieldName, file, body }: UploadRequest): string {
requireUploadAccess(auth); requireUploadAccess(auth);
const originalExtension = extname(file.originalName); const extension = extname(body.filename || file.originalName);
const lookup = { const lookup = {
[UploadFieldName.ASSET_DATA]: originalExtension, [UploadFieldName.ASSET_DATA]: extension,
[UploadFieldName.SIDECAR_DATA]: '.xmp', [UploadFieldName.SIDECAR_DATA]: '.xmp',
[UploadFieldName.PROFILE_DATA]: originalExtension, [UploadFieldName.PROFILE_DATA]: extension,
}; };
return sanitize(`${file.uuid}${lookup[fieldName]}`); return sanitize(`${file.uuid}${lookup[fieldName]}`);
@ -117,8 +111,8 @@ export class AssetMediaService extends BaseService {
} }
async onUploadError(request: AuthRequest, file: Express.Multer.File) { async onUploadError(request: AuthRequest, file: Express.Multer.File) {
const uploadFilename = this.getUploadFilename(asRequest(request, file)); const uploadFilename = this.getUploadFilename(asUploadRequest(request, file));
const uploadFolder = this.getUploadFolder(asRequest(request, file)); const uploadFolder = this.getUploadFolder(asUploadRequest(request, file));
const uploadPath = `${uploadFolder}/${uploadFilename}`; const uploadPath = `${uploadFolder}/${uploadFilename}`;
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [uploadPath] } }); await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [uploadPath] } });

View file

@ -1,5 +1,7 @@
import { SystemConfig } from 'src/config'; import { SystemConfig } from 'src/config';
import { VECTOR_EXTENSIONS } from 'src/constants'; import { VECTOR_EXTENSIONS } from 'src/constants';
import { UploadFieldName } from 'src/dtos/asset-media.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { import {
AssetMetadataKey, AssetMetadataKey,
AssetOrder, AssetOrder,
@ -408,6 +410,16 @@ export interface UploadFile {
size: number; size: number;
} }
export type UploadRequest = {
auth: AuthDto | null;
fieldName: UploadFieldName;
file: UploadFile;
body: {
filename?: string;
[key: string]: unknown;
};
};
export interface UploadFiles { export interface UploadFiles {
assetData: ImmichFile[]; assetData: ImmichFile[];
sidecarData: ImmichFile[]; sidecarData: ImmichFile[];

View file

@ -10,7 +10,7 @@ import { AccessRepository } from 'src/repositories/access.repository';
import { AssetRepository } from 'src/repositories/asset.repository'; import { AssetRepository } from 'src/repositories/asset.repository';
import { EventRepository } from 'src/repositories/event.repository'; import { EventRepository } from 'src/repositories/event.repository';
import { PartnerRepository } from 'src/repositories/partner.repository'; import { PartnerRepository } from 'src/repositories/partner.repository';
import { IBulkAsset, ImmichFile, UploadFile } from 'src/types'; import { IBulkAsset, ImmichFile, UploadFile, UploadRequest } from 'src/types';
import { checkAccess } from 'src/utils/access'; import { checkAccess } from 'src/utils/access';
export const getAssetFile = (files: AssetFile[], type: AssetFileType | GeneratedImageType) => { export const getAssetFile = (files: AssetFile[], type: AssetFileType | GeneratedImageType) => {
@ -190,9 +190,10 @@ export function mapToUploadFile(file: ImmichFile): UploadFile {
}; };
} }
export const asRequest = (request: AuthRequest, file: Express.Multer.File) => { export const asUploadRequest = (request: AuthRequest, file: Express.Multer.File): UploadRequest => {
return { return {
auth: request.user || null, auth: request.user || null,
body: request.body,
fieldName: file.fieldname as UploadFieldName, fieldName: file.fieldname as UploadFieldName,
file: mapToUploadFile(file as ImmichFile), file: mapToUploadFile(file as ImmichFile),
}; };