diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index e36f699f53..ad60e30425 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -1,3 +1,4 @@ +import { DateTime } from 'luxon'; import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; @@ -90,18 +91,23 @@ describe(BackupService.name, () => { it('should remove failed backup files', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); + //`immich-db-backup-${DateTime.now().toFormat("yyyyLLdd'T'HHmmss")}-v${serverVersion.toString()}-pg${databaseVersion.split(' ')[0]}.sql.gz.tmp`, mocks.storage.readdir.mockResolvedValue([ 'immich-db-backup-123.sql.gz.tmp', - 'immich-db-backup-234.sql.gz', - 'immich-db-backup-345.sql.gz.tmp', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-123.sql.gz.tmp`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-345.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250729T110116-v1.234.5-pg14.5.sql.gz.tmp`, ); }); @@ -118,17 +124,21 @@ describe(BackupService.name, () => { it('should remove old backup files over keepLastAmount and failed backups', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.readdir.mockResolvedValue([ - 'immich-db-backup-1.sql.gz.tmp', - 'immich-db-backup-2.sql.gz', - 'immich-db-backup-3.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + 'immich-db-backup-1753789649000.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1753789649000.sql.gz`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-2.sql.gz`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250727T110116-v1.234.5-pg14.5.sql.gz`, ); }); }); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 9daa2c3aea..0948965f1c 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -53,9 +53,14 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); - const failedBackups = files.filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz\.tmp$/)); + const failedBackups = files.filter((file) => file.match(/immich-db-backup-.*\.sql\.gz\.tmp$/)); const backups = files - .filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz$/)) + .filter((file) => { + const oldBackupStyle = file.match(/immich-db-backup-\d+\.sql\.gz$/); + //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz + const newBackupStyle = file.match(/immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); + return oldBackupStyle || newBackupStyle; + }) .sort() .reverse();