diff --git a/server/src/maintenance/maintenance-worker.service.ts b/server/src/maintenance/maintenance-worker.service.ts index 21f4811a86..e96ddc7c8c 100644 --- a/server/src/maintenance/maintenance-worker.service.ts +++ b/server/src/maintenance/maintenance-worker.service.ts @@ -26,8 +26,14 @@ import { type BaseService as _BaseService } from 'src/services/base.service'; import { type DatabaseBackupService as _DatabaseBackupService } from 'src/services/database-backup.service'; import { type ServerService as _ServerService } from 'src/services/server.service'; import { MaintenanceModeState } from 'src/types'; -import { deleteBackups, downloadBackup, listBackups, restoreBackup, uploadBackup } from 'src/utils/backups'; import { getConfig } from 'src/utils/config'; +import { + deleteDatabaseBackup, + downloadDatabaseBackup, + listDatabaseBackups, + restoreDatabaseBackup, + uploadDatabaseBackup, +} from 'src/utils/database-backups'; import { ImmichFileResponse } from 'src/utils/file'; import { createMaintenanceLoginUrl, detectPriorInstall } from 'src/utils/maintenance'; import { getExternalDomain } from 'src/utils/misc'; @@ -171,28 +177,28 @@ export class MaintenanceWorkerService { * {@link _DatabaseBackupService.listBackups} */ async listBackups(): Promise<{ backups: string[] }> { - return { backups: await listBackups(this.backupRepos) }; + return { backups: await listDatabaseBackups(this.backupRepos) }; } /** * {@link _DatabaseBackupService.deleteBackup} */ async deleteBackup(files: string[]): Promise { - return deleteBackups(this.backupRepos, files); + return deleteDatabaseBackup(this.backupRepos, files); } /** * {@link _DatabaseBackupService.uploadBackup} */ async uploadBackup(file: Express.Multer.File): Promise { - return uploadBackup(this.backupRepos, file); + return uploadDatabaseBackup(this.backupRepos, file); } /** * {@link _DatabaseBackupService.downloadBackup} */ downloadBackup(fileName: string): ImmichFileResponse { - return downloadBackup(fileName); + return downloadDatabaseBackup(fileName); } private get backupRepos() { @@ -339,7 +345,7 @@ export class MaintenanceWorkerService { progress: 0, }); - await restoreBackup(this.backupRepos, filename, (task, progress) => + await restoreDatabaseBackup(this.backupRepos, filename, (task, progress) => this.setStatus({ active: true, action: MaintenanceAction.RestoreDatabase, diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 7de5bddeb7..0c4ddfc521 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -6,11 +6,11 @@ import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName, StorageFolde import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; import { - createBackup, - isFailedBackupName, - isValidRoutineBackupName, + createDatabaseBackup, + isFailedDatabaseBackupName, + isValidDatabaseRoutineBackupName, UnsupportedPostgresError, -} from 'src/utils/backups'; +} from 'src/utils/database-backups'; import { handlePromiseError } from 'src/utils/misc'; @Injectable() @@ -57,10 +57,10 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); const backups = files - .filter((fn) => isValidRoutineBackupName(fn)) + .filter((fn) => isValidDatabaseRoutineBackupName(fn)) .toSorted() .toReversed(); - const failedBackups = files.filter((fn) => isFailedBackupName(fn)); + const failedBackups = files.filter((fn) => isFailedDatabaseBackupName(fn)); const toDelete = backups.slice(config.keepLastAmount); toDelete.push(...failedBackups); @@ -74,7 +74,7 @@ export class BackupService extends BaseService { @OnJob({ name: JobName.DatabaseBackup, queue: QueueName.BackupDatabase }) async handleBackupDatabase(): Promise { try { - await createBackup(this.backupRepos); + await createDatabaseBackup(this.backupRepos); } catch (error) { if (error instanceof UnsupportedPostgresError) { return JobStatus.Failed; diff --git a/server/src/services/database-backup.service.ts b/server/src/services/database-backup.service.ts index 1dadc1c63b..66be123a42 100644 --- a/server/src/services/database-backup.service.ts +++ b/server/src/services/database-backup.service.ts @@ -1,6 +1,11 @@ import { Injectable } from '@nestjs/common'; import { BaseService } from 'src/services/base.service'; -import { deleteBackups, downloadBackup, listBackups, uploadBackup } from 'src/utils/backups'; +import { + deleteDatabaseBackup, + downloadDatabaseBackup, + listDatabaseBackups, + uploadDatabaseBackup, +} from 'src/utils/database-backups'; import { ImmichFileResponse } from 'src/utils/file'; /** @@ -9,19 +14,19 @@ import { ImmichFileResponse } from 'src/utils/file'; @Injectable() export class DatabaseBackupService extends BaseService { async listBackups(): Promise<{ backups: string[] }> { - return { backups: await listBackups(this.backupRepos) }; + return { backups: await listDatabaseBackups(this.backupRepos) }; } deleteBackup(files: string[]): Promise { - return deleteBackups(this.backupRepos, files); + return deleteDatabaseBackup(this.backupRepos, files); } async uploadBackup(file: Express.Multer.File): Promise { - return uploadBackup(this.backupRepos, file); + return uploadDatabaseBackup(this.backupRepos, file); } downloadBackup(fileName: string): ImmichFileResponse { - return downloadBackup(fileName); + return downloadDatabaseBackup(fileName); } private get backupRepos() { diff --git a/server/src/utils/backups.ts b/server/src/utils/database-backups.ts similarity index 91% rename from server/src/utils/backups.ts rename to server/src/utils/database-backups.ts index d8f96a4cef..a6aad17935 100644 --- a/server/src/utils/backups.ts +++ b/server/src/utils/database-backups.ts @@ -14,18 +14,18 @@ import { LoggingRepository } from 'src/repositories/logging.repository'; import { ProcessRepository } from 'src/repositories/process.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; -export function isValidBackupName(filename: string) { +export function isValidDatabaseBackupName(filename: string) { return filename.match(/^[\d\w-.]+\.sql(?:\.gz)?$/); } -export function isValidRoutineBackupName(filename: string) { +export function isValidDatabaseRoutineBackupName(filename: string) { const oldBackupStyle = filename.match(/^immich-db-backup-\d+\.sql\.gz$/); //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz const newBackupStyle = filename.match(/^immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); return oldBackupStyle || newBackupStyle; } -export function isFailedBackupName(filename: string) { +export function isFailedDatabaseBackupName(filename: string) { return filename.match(/^immich-db-backup-.*\.sql\.gz\.tmp$/); } @@ -137,7 +137,7 @@ export async function buildPostgresLaunchArguments( }; } -export async function createBackup( +export async function createDatabaseBackup( { logger, storage, process: processRepository, ...pgRepos }: BackupRepos, filenamePrefix: string = '', ): Promise { @@ -179,7 +179,7 @@ export async function createBackup( logger.log(`Database Backup Success`); } -export async function restoreBackup( +export async function restoreDatabaseBackup( { logger, storage, process: processRepository, ...pgRepos }: BackupRepos, filename: string, progressCb?: (action: 'backup' | 'restore', progress: number) => void, @@ -188,7 +188,7 @@ export async function restoreBackup( let complete = false; try { - if (!isValidBackupName(filename)) { + if (!isValidDatabaseBackupName(filename)) { throw new Error('Invalid backup file format!'); } @@ -202,7 +202,7 @@ export async function restoreBackup( progressCb?.('backup', 0.05); - await createBackup({ logger, storage, process: processRepository, ...pgRepos }, 'restore-point-'); + await createDatabaseBackup({ logger, storage, process: processRepository, ...pgRepos }, 'restore-point-'); logger.log(`Database Restore Starting. Database Version: ${databaseMajorVersion}`); @@ -266,32 +266,32 @@ export async function restoreBackup( logger.log(`Database Restore Success`); } -export async function deleteBackups({ storage }: Pick, files: string[]): Promise { +export async function deleteDatabaseBackup({ storage }: Pick, files: string[]): Promise { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); - if (files.some((filename) => !isValidBackupName(filename))) { + if (files.some((filename) => !isValidDatabaseBackupName(filename))) { throw new BadRequestException('Invalid backup name!'); } await Promise.all(files.map((filename) => storage.unlink(path.join(backupsFolder, filename)))); } -export async function listBackups({ storage }: Pick): Promise { +export async function listDatabaseBackups({ storage }: Pick): Promise { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await storage.readdir(backupsFolder); return files - .filter((fn) => isValidBackupName(fn)) + .filter((fn) => isValidDatabaseBackupName(fn)) .toSorted((a, b) => (a.startsWith('uploaded-') === b.startsWith('uploaded-') ? a.localeCompare(b) : 1)) .toReversed(); } -export async function uploadBackup( +export async function uploadDatabaseBackup( { storage }: Pick, file: Express.Multer.File, ): Promise { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const fn = basename(file.originalname); - if (!isValidBackupName(fn)) { + if (!isValidDatabaseBackupName(fn)) { throw new BadRequestException('Invalid backup name!'); } @@ -299,8 +299,8 @@ export async function uploadBackup( await storage.createOrOverwriteFile(path, file.buffer); } -export function downloadBackup(fileName: string) { - if (!isValidBackupName(fileName)) { +export function downloadDatabaseBackup(fileName: string) { + if (!isValidDatabaseBackupName(fileName)) { throw new BadRequestException('Invalid backup name!'); }