refactor: scope util to database-backups instead of backups

This commit is contained in:
izzy
2025-12-03 12:30:07 +00:00
parent adc2d5d1e5
commit 4e2187acf9
4 changed files with 44 additions and 33 deletions

View File

@@ -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<void> {
return deleteBackups(this.backupRepos, files);
return deleteDatabaseBackup(this.backupRepos, files);
}
/**
* {@link _DatabaseBackupService.uploadBackup}
*/
async uploadBackup(file: Express.Multer.File): Promise<void> {
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,

View File

@@ -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<JobStatus> {
try {
await createBackup(this.backupRepos);
await createDatabaseBackup(this.backupRepos);
} catch (error) {
if (error instanceof UnsupportedPostgresError) {
return JobStatus.Failed;

View File

@@ -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<void> {
return deleteBackups(this.backupRepos, files);
return deleteDatabaseBackup(this.backupRepos, files);
}
async uploadBackup(file: Express.Multer.File): Promise<void> {
return uploadBackup(this.backupRepos, file);
return uploadDatabaseBackup(this.backupRepos, file);
}
downloadBackup(fileName: string): ImmichFileResponse {
return downloadBackup(fileName);
return downloadDatabaseBackup(fileName);
}
private get backupRepos() {

View File

@@ -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<void> {
@@ -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<BackupRepos, 'storage'>, files: string[]): Promise<void> {
export async function deleteDatabaseBackup({ storage }: Pick<BackupRepos, 'storage'>, files: string[]): Promise<void> {
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<BackupRepos, 'storage'>): Promise<string[]> {
export async function listDatabaseBackups({ storage }: Pick<BackupRepos, 'storage'>): Promise<string[]> {
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<BackupRepos, 'storage'>,
file: Express.Multer.File,
): Promise<void> {
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!');
}