fix(server): double extension when filename has uppercase extension (#17226)

* fix(server): double extension when filename has uppercase extension

* Proper tests
This commit is contained in:
Alex 2025-03-31 09:16:04 -05:00 committed by GitHub
parent b25914c2a5
commit b8b2898c87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 1 deletions

View File

@ -548,4 +548,102 @@ describe(StorageTemplateService.name, () => {
expect(mocks.asset.update).not.toHaveBeenCalled();
});
});
describe('file rename correctness', () => {
it('should not create double extensions when filename has lower extension', async () => {
const asset = assetStub.storageAsset({
originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.heic',
originalFileName: 'IMG_7065.HEIC',
});
mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset]));
mocks.user.getList.mockResolvedValue([userStub.storageLabel]);
mocks.move.create.mockResolvedValue({
id: '123',
entityId: asset.id,
pathType: AssetPathType.ORIGINAL,
oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.heic',
newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.heic',
});
await sut.handleMigration();
expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled();
expect(mocks.storage.rename).toHaveBeenCalledWith(
'upload/library/user-id/2022/2022-06-19/IMG_7065.heic',
'upload/library/label-1/2022/2022-06-19/IMG_7065.heic',
);
});
it('should not create double extensions when filename has uppercase extension', async () => {
const asset = assetStub.storageAsset({
originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC',
originalFileName: 'IMG_7065.HEIC',
});
mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset]));
mocks.user.getList.mockResolvedValue([userStub.storageLabel]);
mocks.move.create.mockResolvedValue({
id: '123',
entityId: asset.id,
pathType: AssetPathType.ORIGINAL,
oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC',
newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.heic',
});
await sut.handleMigration();
expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled();
expect(mocks.storage.rename).toHaveBeenCalledWith(
'upload/library/user-id/2022/2022-06-19/IMG_7065.HEIC',
'upload/library/label-1/2022/2022-06-19/IMG_7065.heic',
);
});
it('should normalize the filename to lowercase (JPEG > jpg)', async () => {
const asset = assetStub.storageAsset({
originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG',
originalFileName: 'IMG_7065.JPEG',
});
mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset]));
mocks.user.getList.mockResolvedValue([userStub.storageLabel]);
mocks.move.create.mockResolvedValue({
id: '123',
entityId: asset.id,
pathType: AssetPathType.ORIGINAL,
oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG',
newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.jpg',
});
await sut.handleMigration();
expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled();
expect(mocks.storage.rename).toHaveBeenCalledWith(
'upload/library/user-id/2022/2022-06-19/IMG_7065.JPEG',
'upload/library/label-1/2022/2022-06-19/IMG_7065.jpg',
);
});
it('should normalize the filename to lowercase (JPG > jpg)', async () => {
const asset = assetStub.storageAsset({
originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG',
originalFileName: 'IMG_7065.JPG',
});
mocks.asset.streamStorageTemplateAssets.mockReturnValue(makeStream([asset]));
mocks.user.getList.mockResolvedValue([userStub.storageLabel]);
mocks.move.create.mockResolvedValue({
id: '123',
entityId: asset.id,
pathType: AssetPathType.ORIGINAL,
oldPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG',
newPath: 'upload/library/user-id/2023/2023-02-23/IMG_7065.jpg',
});
await sut.handleMigration();
expect(mocks.asset.streamStorageTemplateAssets).toHaveBeenCalled();
expect(mocks.storage.rename).toHaveBeenCalledWith(
'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG',
'upload/library/label-1/2022/2022-06-19/IMG_7065.jpg',
);
});
});
});

View File

@ -220,9 +220,11 @@ export class StorageTemplateService extends BaseService {
const { storageLabel, filename } = metadata;
try {
const filenameWithoutExtension = path.basename(filename, path.extname(filename));
const source = asset.originalPath;
let extension = path.extname(source).split('.').pop() as string;
const sanitized = sanitize(path.basename(filename, `.${extension}`));
const sanitized = sanitize(path.basename(filenameWithoutExtension, `.${extension}`));
extension = extension?.toLowerCase();
const rootPath = StorageCore.getLibraryFolder({ id: asset.ownerId, storageLabel });