mirror of
https://github.com/immich-app/immich.git
synced 2024-11-28 09:33:27 +02:00
fix(server): skip smtp validation if unchanged (#12111)
* fix(server): skip smtp validation if unchanged * update comparison + convert config to plain object
This commit is contained in:
parent
d08a20bd57
commit
74f18a4523
@ -1,4 +1,6 @@
|
|||||||
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { defaults, SystemConfig } from 'src/config';
|
import { defaults, SystemConfig } from 'src/config';
|
||||||
|
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
||||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
||||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
||||||
import { AssetFileType, UserMetadataKey } from 'src/enum';
|
import { AssetFileType, UserMetadataKey } from 'src/enum';
|
||||||
@ -112,6 +114,14 @@ describe(NotificationService.name, () => {
|
|||||||
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips smtp validation with DTO when there are no changes', async () => {
|
||||||
|
const oldConfig = { ...configs.smtpEnabled };
|
||||||
|
const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled);
|
||||||
|
|
||||||
|
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||||
|
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('skips smtp validation when smtp is disabled', async () => {
|
it('skips smtp validation when smtp is disabled', async () => {
|
||||||
const oldConfig = { ...configs.smtpEnabled };
|
const oldConfig = { ...configs.smtpEnabled };
|
||||||
const newConfig = { ...configs.smtpDisabled };
|
const newConfig = { ...configs.smtpDisabled };
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||||
import { isEqual } from 'lodash';
|
|
||||||
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
|
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { OnEmit } from 'src/decorators';
|
import { OnEmit } from 'src/decorators';
|
||||||
@ -23,6 +22,7 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf
|
|||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { getFilenameExtension } from 'src/utils/file';
|
import { getFilenameExtension } from 'src/utils/file';
|
||||||
|
import { isEqualObject } from 'src/utils/object';
|
||||||
import { getPreferences } from 'src/utils/preferences';
|
import { getPreferences } from 'src/utils/preferences';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -47,7 +47,7 @@ export class NotificationService {
|
|||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
newConfig.notifications.smtp.enabled &&
|
newConfig.notifications.smtp.enabled &&
|
||||||
!isEqual(oldConfig.notifications.smtp, newConfig.notifications.smtp)
|
!isEqualObject(oldConfig.notifications.smtp, newConfig.notifications.smtp)
|
||||||
) {
|
) {
|
||||||
await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport);
|
await this.notificationRepository.verifySmtp(newConfig.notifications.smtp.transport);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from
|
|||||||
import { ArgOf, ClientEvent, IEventRepository, ServerEvent } from 'src/interfaces/event.interface';
|
import { ArgOf, ClientEvent, IEventRepository, ServerEvent } from 'src/interfaces/event.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
|
import { toPlainObject } from 'src/utils/object';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SystemConfigService {
|
export class SystemConfigService {
|
||||||
@ -63,7 +64,7 @@ export class SystemConfigService {
|
|||||||
const oldConfig = await this.core.getConfig({ withCache: false });
|
const oldConfig = await this.core.getConfig({ withCache: false });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.eventRepository.emit('config.validate', { newConfig: dto, oldConfig });
|
await this.eventRepository.emit('config.validate', { newConfig: toPlainObject(dto), oldConfig });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn(`Unable to save system config due to a validation error: ${error}`);
|
this.logger.warn(`Unable to save system config due to a validation error: ${error}`);
|
||||||
throw new BadRequestException(error instanceof Error ? error.message : error);
|
throw new BadRequestException(error instanceof Error ? error.message : error);
|
||||||
|
15
server/src/utils/object.ts
Normal file
15
server/src/utils/object.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { isEqual, isPlainObject } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deeply clones and converts a class instance to a plain object.
|
||||||
|
*/
|
||||||
|
export function toPlainObject<T extends object>(obj: T): T {
|
||||||
|
return isPlainObject(obj) ? obj : structuredClone(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a deep comparison between objects, converting them to plain objects first if needed.
|
||||||
|
*/
|
||||||
|
export function isEqualObject(value: object, other: object): boolean {
|
||||||
|
return isEqual(toPlainObject(value), toPlainObject(other));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user