mirror of
https://github.com/immich-app/immich
synced 2026-08-22 13:13:05 +00:00
refactor: use service instead of repository
chore: read token from cookie if possible chore: rename client event to AppRestartV1
This commit is contained in:
parent
f2b941b066
commit
c8f3eef45d
9 changed files with 51 additions and 44 deletions
|
|
@ -1,46 +1,42 @@
|
|||
import { BadRequestException, Body, Controller, Get, Post, Res } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Post, Req, Res } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { MaintenanceAuthDto, MaintenanceLoginDto, MaintenanceModeResponseDto } from 'src/dtos/maintenance.dto';
|
||||
import { Request, Response } from 'express';
|
||||
import { MaintenanceAuthDto, MaintenanceLoginDto } from 'src/dtos/maintenance.dto';
|
||||
import { ServerConfigDto } from 'src/dtos/server.dto';
|
||||
import { ImmichCookie } from 'src/enum';
|
||||
import { MaintenanceRoute } from 'src/middleware/maintenance-auth.guard';
|
||||
import { MaintenanceWorkerRepository } from 'src/repositories/maintenance-worker.repository';
|
||||
import { MaintenanceWorkerService } from 'src/services/maintenance-worker.service';
|
||||
|
||||
@ApiTags('Maintenance (admin)')
|
||||
@Controller('')
|
||||
@Controller()
|
||||
export class MaintenanceWorkerController {
|
||||
constructor(
|
||||
private repository: MaintenanceWorkerRepository,
|
||||
private service: MaintenanceWorkerService,
|
||||
) {}
|
||||
constructor(private service: MaintenanceWorkerService) {}
|
||||
|
||||
@Get('server/config')
|
||||
getServerConfig(): Promise<ServerConfigDto> {
|
||||
return this.service.getSystemConfig();
|
||||
}
|
||||
|
||||
@Post('admin/maintenance/start')
|
||||
@MaintenanceRoute()
|
||||
startMaintenance(): Promise<MaintenanceModeResponseDto> {
|
||||
throw new BadRequestException('Already in maintenance mode');
|
||||
}
|
||||
|
||||
@Post('admin/maintenance/login')
|
||||
async maintenanceLogin(
|
||||
@Req() request: Request,
|
||||
@Body() dto: MaintenanceLoginDto,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
): Promise<MaintenanceAuthDto> {
|
||||
const auth = await this.repository.decodeToken(dto.token);
|
||||
const auth = await this.service.login(dto.token ?? request.cookies[ImmichCookie.MaintenanceToken]);
|
||||
response.cookie(ImmichCookie.MaintenanceToken, dto.token);
|
||||
return auth;
|
||||
}
|
||||
|
||||
@Post('admin/maintenance/start')
|
||||
@MaintenanceRoute()
|
||||
startMaintenance(): void {
|
||||
this.service.startMaintenance();
|
||||
}
|
||||
|
||||
@Post('admin/maintenance/end')
|
||||
@MaintenanceRoute()
|
||||
async endMaintenance(): Promise<MaintenanceModeResponseDto> {
|
||||
async endMaintenance(): Promise<void> {
|
||||
await this.service.endMaintenance();
|
||||
return { isMaintenanceMode: false };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export class MaintenanceModeResponseDto {
|
|||
|
||||
export class MaintenanceLoginDto {
|
||||
@ValidateString()
|
||||
token!: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export class MaintenanceAuthDto {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { IncomingHttpHeaders } from 'node:http';
|
|||
import { Server, Socket } from 'socket.io';
|
||||
import { MaintenanceAuthDto, MaintenanceModeResponseDto } from 'src/dtos/maintenance.dto';
|
||||
import { ExitCode, ImmichCookie, SystemMetadataKey } from 'src/enum';
|
||||
import { ArgsOf } from 'src/repositories/event.repository';
|
||||
import { AppRestartEvent, ArgsOf } from 'src/repositories/event.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||
import { MaintenanceModeState } from 'src/types';
|
||||
|
|
@ -26,7 +26,7 @@ export const serverEvents = ['AppRestart'] as const;
|
|||
export type ServerEvents = (typeof serverEvents)[number];
|
||||
|
||||
export interface ClientEventMap {
|
||||
on_server_restart: [MaintenanceModeResponseDto];
|
||||
AppRestartV1: [AppRestartEvent];
|
||||
}
|
||||
|
||||
@WebSocketGateway({
|
||||
|
|
@ -91,10 +91,10 @@ export class MaintenanceWorkerRepository implements OnGatewayConnection, OnGatew
|
|||
|
||||
async authenticate(headers: IncomingHttpHeaders): Promise<MaintenanceAuthDto> {
|
||||
const jwtToken = parse(headers.cookie || '')[ImmichCookie.MaintenanceToken];
|
||||
return this.decodeToken(jwtToken);
|
||||
return this.authenticateToken(jwtToken);
|
||||
}
|
||||
|
||||
async decodeToken(jwtToken?: string): Promise<MaintenanceAuthDto> {
|
||||
async authenticateToken(jwtToken?: string): Promise<MaintenanceAuthDto> {
|
||||
if (!jwtToken) {
|
||||
throw new UnauthorizedException('Missing JWT Token');
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ export class MaintenanceWorkerRepository implements OnGatewayConnection, OnGatew
|
|||
}
|
||||
|
||||
restartApp(state: MaintenanceModeResponseDto) {
|
||||
this.clientBroadcast('on_server_restart', state);
|
||||
this.clientBroadcast('AppRestartV1', state);
|
||||
this.serverSend('AppRestart', state);
|
||||
this.exitApp();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class MaintenanceRepository {
|
|||
server.adapter(createAdapter(pubClient, subClient));
|
||||
|
||||
// => corresponds to notification.service.ts#onAppRestart
|
||||
server.emit('on_server_restart', state, () => {
|
||||
server.emit('AppRestartV1', state, () => {
|
||||
server.serverSideEmit('AppRestart', state, () => {
|
||||
pubClient.disconnect();
|
||||
subClient.disconnect();
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import {
|
|||
import { Server, Socket } from 'socket.io';
|
||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { MaintenanceModeResponseDto } from 'src/dtos/maintenance.dto';
|
||||
import { NotificationDto } from 'src/dtos/notification.dto';
|
||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
||||
import { SyncAssetExifV1, SyncAssetV1 } from 'src/dtos/sync.dto';
|
||||
import { ArgsOf, EventRepository } from 'src/repositories/event.repository';
|
||||
import { AppRestartEvent, ArgsOf, EventRepository } from 'src/repositories/event.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { handlePromiseError } from 'src/utils/misc';
|
||||
|
||||
|
|
@ -31,13 +30,13 @@ export interface ClientEventMap {
|
|||
on_asset_stack_update: string[];
|
||||
on_person_thumbnail: [string];
|
||||
on_server_version: [ServerVersionResponseDto];
|
||||
on_server_restart: [MaintenanceModeResponseDto];
|
||||
on_config_update: [];
|
||||
on_new_release: [ReleaseNotification];
|
||||
on_notification: [NotificationDto];
|
||||
on_session_delete: [string];
|
||||
|
||||
AssetUploadReadyV1: [{ asset: SyncAssetV1; exif: SyncAssetExifV1 }];
|
||||
AppRestartV1: [AppRestartEvent];
|
||||
}
|
||||
|
||||
export type AuthFn = (client: Socket) => Promise<AuthDto>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { MaintenanceAuthDto } from 'src/dtos/maintenance.dto';
|
||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { MaintenanceWorkerRepository } from 'src/repositories/maintenance-worker.repository';
|
||||
|
|
@ -49,6 +50,18 @@ export class MaintenanceWorkerService {
|
|||
};
|
||||
}
|
||||
|
||||
logSecret() {
|
||||
void this.maintenanceRepository.logSecret();
|
||||
}
|
||||
|
||||
login(jwt?: string): Promise<MaintenanceAuthDto> {
|
||||
return this.maintenanceRepository.authenticateToken(jwt);
|
||||
}
|
||||
|
||||
startMaintenance(): Promise<void> {
|
||||
throw new BadRequestException('Already in maintenance mode');
|
||||
}
|
||||
|
||||
async endMaintenance(): Promise<void> {
|
||||
await this.maintenanceRepository.exitMaintenanceMode();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export class NotificationService extends BaseService {
|
|||
|
||||
@OnEvent({ name: 'AppRestart' })
|
||||
onAppRestart(state: ArgOf<'AppRestart'>) {
|
||||
this.websocketRepository.clientBroadcast('on_server_restart', {
|
||||
this.websocketRepository.clientBroadcast('AppRestartV1', {
|
||||
isMaintenanceMode: state.isMaintenanceMode,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,23 +3,21 @@ import { NestExpressApplication } from '@nestjs/platform-express';
|
|||
import { configureExpress, configureTelemetry } from 'src/app.common';
|
||||
import { MaintenanceModule } from 'src/app.module';
|
||||
import { MaintenanceWorkerRepository } from 'src/repositories/maintenance-worker.repository';
|
||||
import { MaintenanceWorkerService } from 'src/services/maintenance-worker.service';
|
||||
import { isStartUpError } from 'src/utils/misc';
|
||||
|
||||
async function bootstrap() {
|
||||
process.title = 'immich-maintenance';
|
||||
|
||||
configureTelemetry();
|
||||
|
||||
const app = await NestFactory.create<NestExpressApplication>(MaintenanceModule, { bufferLogs: true });
|
||||
const maintenanceWorkerRepository = app.get(MaintenanceWorkerRepository);
|
||||
maintenanceWorkerRepository.setCloseFn(() => app.close());
|
||||
|
||||
app.get(MaintenanceWorkerRepository).setCloseFn(() => app.close());
|
||||
void configureExpress(app, {
|
||||
permitSwaggerWrite: false,
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
void maintenanceWorkerRepository.logSecret();
|
||||
app.get(MaintenanceWorkerService).logSecret();
|
||||
}
|
||||
|
||||
bootstrap().catch((error) => {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,7 @@ import { AppRoute } from '$lib/constants';
|
|||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||
import { createEventEmitter } from '$lib/utils/eventemitter';
|
||||
import {
|
||||
type AssetResponseDto,
|
||||
type MaintenanceModeResponseDto,
|
||||
type NotificationDto,
|
||||
type ServerVersionResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { type AssetResponseDto, type NotificationDto, type ServerVersionResponseDto } from '@immich/sdk';
|
||||
import { io, type Socket } from 'socket.io-client';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import { user } from './user.store';
|
||||
|
|
@ -20,6 +15,11 @@ export interface ReleaseEvent {
|
|||
serverVersion: ServerVersionResponseDto;
|
||||
releaseVersion: ServerVersionResponseDto;
|
||||
}
|
||||
|
||||
interface AppRestartEvent {
|
||||
isMaintenanceMode: boolean;
|
||||
}
|
||||
|
||||
export interface Events {
|
||||
on_upload_success: (asset: AssetResponseDto) => void;
|
||||
on_user_delete: (id: string) => void;
|
||||
|
|
@ -31,11 +31,12 @@ export interface Events {
|
|||
on_asset_stack_update: (assetIds: string[]) => void;
|
||||
on_person_thumbnail: (personId: string) => void;
|
||||
on_server_version: (serverVersion: ServerVersionResponseDto) => void;
|
||||
on_server_restart: (maintenanceMode: MaintenanceModeResponseDto) => void;
|
||||
on_config_update: () => void;
|
||||
on_new_release: (newRelease: ReleaseEvent) => void;
|
||||
on_session_delete: (sessionId: string) => void;
|
||||
on_notification: (notification: NotificationDto) => void;
|
||||
|
||||
AppRestartV1: (event: AppRestartEvent) => void;
|
||||
}
|
||||
|
||||
const websocket: Socket<Events> = io({
|
||||
|
|
@ -50,7 +51,7 @@ export const websocketStore = {
|
|||
connected: writable<boolean>(false),
|
||||
serverVersion: writable<ServerVersionResponseDto>(),
|
||||
release: writable<ReleaseEvent>(),
|
||||
serverRestarting: writable<undefined | MaintenanceModeResponseDto>(),
|
||||
serverRestarting: writable<undefined | AppRestartEvent>(),
|
||||
};
|
||||
|
||||
export const websocketEvents = createEventEmitter(websocket);
|
||||
|
|
@ -59,7 +60,7 @@ websocket
|
|||
.on('connect', () => websocketStore.connected.set(true))
|
||||
.on('disconnect', () => websocketStore.connected.set(false))
|
||||
.on('on_server_version', (serverVersion) => websocketStore.serverVersion.set(serverVersion))
|
||||
.on('on_server_restart', (mode) => websocketStore.serverRestarting.set(mode))
|
||||
.on('AppRestartV1', (mode) => websocketStore.serverRestarting.set(mode))
|
||||
.on('on_new_release', (releaseVersion) => websocketStore.release.set(releaseVersion))
|
||||
.on('on_session_delete', () => authManager.logout())
|
||||
.on('on_notification', () => notificationManager.refresh())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue