refactor: move maintenance resolution code to static method in service

This commit is contained in:
izzy 2025-11-03 10:56:38 +00:00
parent 018af805e6
commit 58c4a41eed
No known key found for this signature in database
GPG key ID: 5059F398521BB0F6
3 changed files with 37 additions and 25 deletions

View file

@ -3,11 +3,11 @@ import { CommandFactory } from 'nest-commander';
import { ChildProcess, fork } from 'node:child_process';
import { dirname, join } from 'node:path';
import { Worker } from 'node:worker_threads';
import { PostgresError } from 'postgres';
import { ApiModule, ImmichAdminModule } from 'src/app.module';
import { ImmichWorker, LogLevel, SystemMetadataKey } from 'src/enum';
import { ImmichWorker, LogLevel } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { MaintenanceService } from 'src/services/maintenance.service';
/**
* Manages worker lifecycle
@ -47,22 +47,13 @@ class Workers {
* Initialise a short-lived Nest application to build configuration
* @returns System configuration
*/
private async getConfig(): Promise<{ isMaintenanceMode?: boolean }> {
try {
const app = await NestFactory.create(ApiModule);
const metadataRepo = app.get(SystemMetadataRepository);
private async getConfig(): Promise<{ isMaintenanceMode: boolean }> {
const app = await NestFactory.create(ApiModule);
const metadataRepository = app.get(SystemMetadataRepository);
await app.close();
await app.close();
return metadataRepo.get(SystemMetadataKey.MaintenanceMode).then((value) => value ?? {});
} catch (err) {
// Table doesn't exist (migrations haven't run yet)
if (err instanceof PostgresError && err.code === '42P01') {
return { isMaintenanceMode: false };
}
throw err;
}
return await MaintenanceService.getMaintenanceModeWith(metadataRepository);
}
/**

View file

@ -1,16 +1,33 @@
import { BadRequestException, INestApplication, Injectable } from '@nestjs/common';
import { PostgresError } from 'postgres';
import { OnEvent } from 'src/decorators';
import { MaintenanceModeResponseDto } from 'src/dtos/maintenance.dto';
import { SystemMetadataKey } from 'src/enum';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { BaseService } from 'src/services/base.service';
@Injectable()
export class MaintenanceService extends BaseService {
nestApplication: INestApplication | undefined;
async getMaintenanceMode(): Promise<MaintenanceModeResponseDto> {
const value = await this.systemMetadataRepository.get(SystemMetadataKey.MaintenanceMode);
return { isMaintenanceMode: false, ...value };
static getMaintenanceModeWith(
systemMetadataRepository: SystemMetadataRepository,
): Promise<{ isMaintenanceMode: boolean }> {
return systemMetadataRepository
.get(SystemMetadataKey.MaintenanceMode)
.catch((err) => {
// Table doesn't exist (migrations haven't run yet)
if (err instanceof PostgresError && err.code === '42P01') {
return { isMaintenanceMode: false };
}
throw err;
})
.then((value) => ({ ...value, isMaintenanceMode: false }));
}
getMaintenanceMode(): Promise<MaintenanceModeResponseDto> {
return MaintenanceService.getMaintenanceModeWith(this.systemMetadataRepository);
}
private async setMaintenanceMode(isMaintenanceMode: boolean) {
@ -18,7 +35,7 @@ export class MaintenanceService extends BaseService {
await this.systemMetadataRepository.set(SystemMetadataKey.MaintenanceMode, state);
this.websocketRepository.clientBroadcast('on_server_restart', state);
this.websocketRepository.serverSend('AppRestart');
this.eventRepository.emit('AppRestart');
await this.eventRepository.emit('AppRestart');
}
async startMaintenance(): Promise<void> {
@ -27,7 +44,7 @@ export class MaintenanceService extends BaseService {
throw new BadRequestException('Already in maintenance mode');
}
this.setMaintenanceMode(true);
await this.setMaintenanceMode(true);
}
async endMaintenance(): Promise<void> {
@ -36,16 +53,19 @@ export class MaintenanceService extends BaseService {
throw new BadRequestException('Not in maintenance mode');
}
this.setMaintenanceMode(false);
await this.setMaintenanceMode(false);
}
@OnEvent({ name: 'AppRestart', server: true })
onRestart() {
this.nestApplication
/* eslint-disable unicorn/no-process-exit */
// we need to specify the exact exit code
void this.nestApplication
?.close() // attempt graceful shutdown
.then(() => process.exit(7)); // then signal restart
// in some exceptional circumstances, close() may hang
setTimeout(() => process.exit(7), 5000);
/* eslint-enable unicorn/no-process-exit */
}
}

View file

@ -17,6 +17,7 @@ import {
import { StorageFolder, SystemMetadataKey } from 'src/enum';
import { UserStatsQueryResponse } from 'src/repositories/user.repository';
import { BaseService } from 'src/services/base.service';
import { MaintenanceService } from 'src/services/maintenance.service';
import { asHumanReadable } from 'src/utils/bytes';
import { mimeTypes } from 'src/utils/mime-types';
import {
@ -118,7 +119,7 @@ export class ServerService extends BaseService {
const config = await this.getConfig({ withCache: false });
const isInitialized = await this.userRepository.hasAdmin();
const onboarding = await this.systemMetadataRepository.get(SystemMetadataKey.AdminOnboarding);
const maintenanceMode = await this.systemMetadataRepository.get(SystemMetadataKey.MaintenanceMode);
const { isMaintenanceMode } = await MaintenanceService.getMaintenanceModeWith(this.systemMetadataRepository);
return {
loginPageMessage: config.server.loginPageMessage,
@ -131,7 +132,7 @@ export class ServerService extends BaseService {
publicUsers: config.server.publicUsers,
mapDarkStyleUrl: config.map.darkStyle,
mapLightStyleUrl: config.map.lightStyle,
maintenanceMode: maintenanceMode?.isMaintenanceMode || false,
maintenanceMode: isMaintenanceMode,
};
}