mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	fix(server): make system config core singleton (#4392)
* make system config core singleton * refactor * fix tests * chore: fix tests --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
		@@ -77,7 +77,7 @@ export class AssetService {
 | 
			
		||||
  ) {
 | 
			
		||||
    this.access = new AccessCore(accessRepository);
 | 
			
		||||
    this.storageCore = new StorageCore(storageRepository);
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  canUploadFile({ authUser, fieldName, file }: UploadRequest): true {
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ export class AuthService {
 | 
			
		||||
    @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
 | 
			
		||||
    @Inject(IKeyRepository) private keyRepository: IKeyRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
    this.userCore = new UserCore(userRepository, libraryRepository, cryptoRepository);
 | 
			
		||||
 | 
			
		||||
    custom.setHttpOptionsDefaults({ timeout: 30000 });
 | 
			
		||||
 
 | 
			
		||||
@@ -223,8 +223,7 @@ describe(JobService.name, () => {
 | 
			
		||||
    it('should subscribe to config changes', async () => {
 | 
			
		||||
      await sut.registerHandlers(makeMockHandlers(false));
 | 
			
		||||
 | 
			
		||||
      const configCore = new SystemConfigCore(newSystemConfigRepositoryMock());
 | 
			
		||||
      configCore.config$.next({
 | 
			
		||||
      SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({
 | 
			
		||||
        job: {
 | 
			
		||||
          [QueueName.BACKGROUND_TASK]: { concurrency: 10 },
 | 
			
		||||
          [QueueName.CLIP_ENCODING]: { concurrency: 10 },
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ export class JobService {
 | 
			
		||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
			
		||||
    @Inject(IPersonRepository) private personRepository: IPersonRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async handleCommand(queueName: QueueName, dto: JobCommandDto): Promise<JobStatusDto> {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ export class MediaService {
 | 
			
		||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
			
		||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
    this.storageCore = new StorageCore(this.storageRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ export class MetadataService {
 | 
			
		||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.storageCore = new StorageCore(storageRepository);
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
    this.configCore.config$.subscribe(() => this.init());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ export class PersonService {
 | 
			
		||||
  ) {
 | 
			
		||||
    this.access = new AccessCore(accessRepository);
 | 
			
		||||
    this.storageCore = new StorageCore(storageRepository);
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getAll(authUser: AuthUserDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ export class SearchService {
 | 
			
		||||
    @Inject(ISearchRepository) private searchRepository: ISearchRepository,
 | 
			
		||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  teardown() {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ export class ServerInfoService {
 | 
			
		||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
			
		||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
    this.storageCore = new StorageCore(storageRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ export class SmartInfoService {
 | 
			
		||||
    @Inject(ISmartInfoRepository) private repository: ISmartInfoRepository,
 | 
			
		||||
    @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async handleQueueObjectTagging({ force }: IBaseJob) {
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ export class StorageTemplateService {
 | 
			
		||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.storageTemplate = this.compile(config.storageTemplate.template);
 | 
			
		||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
			
		||||
    this.configCore = SystemConfigCore.create(configRepository);
 | 
			
		||||
    this.configCore.addValidator((config) => this.validate(config));
 | 
			
		||||
    this.configCore.config$.subscribe((config) => this.onConfig(config));
 | 
			
		||||
    this.storageCore = new StorageCore(storageRepository);
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,7 @@ export enum FeatureFlag {
 | 
			
		||||
 | 
			
		||||
export type FeatureFlags = Record<FeatureFlag, boolean>;
 | 
			
		||||
 | 
			
		||||
const singleton = new Subject<SystemConfig>();
 | 
			
		||||
let instance: SystemConfigCore | null;
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class SystemConfigCore {
 | 
			
		||||
@@ -142,9 +142,20 @@ export class SystemConfigCore {
 | 
			
		||||
  private validators: SystemConfigValidator[] = [];
 | 
			
		||||
  private configCache: SystemConfig | null = null;
 | 
			
		||||
 | 
			
		||||
  public config$ = singleton;
 | 
			
		||||
  public config$ = new Subject<SystemConfig>();
 | 
			
		||||
 | 
			
		||||
  constructor(private repository: ISystemConfigRepository) {}
 | 
			
		||||
  private constructor(private repository: ISystemConfigRepository) {}
 | 
			
		||||
 | 
			
		||||
  static create(repository: ISystemConfigRepository) {
 | 
			
		||||
    if (!instance) {
 | 
			
		||||
      instance = new SystemConfigCore(repository);
 | 
			
		||||
    }
 | 
			
		||||
    return instance;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static reset() {
 | 
			
		||||
    instance = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async requireFeature(feature: FeatureFlag) {
 | 
			
		||||
    const hasFeature = await this.hasFeature(feature);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ import {
 | 
			
		||||
} from '@app/infra/entities';
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { newCommunicationRepositoryMock, newJobRepositoryMock, newSystemConfigRepositoryMock } from '@test';
 | 
			
		||||
import { ICommunicationRepository } from '..';
 | 
			
		||||
import { ICommunicationRepository } from '../communication';
 | 
			
		||||
import { IJobRepository, JobName, QueueName } from '../job';
 | 
			
		||||
import { SystemConfigValidator, defaults } from './system-config.core';
 | 
			
		||||
import { ISystemConfigRepository } from './system-config.repository';
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ export class SystemConfigService {
 | 
			
		||||
    @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository,
 | 
			
		||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.core = new SystemConfigCore(repository);
 | 
			
		||||
    this.core = SystemConfigCore.create(repository);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get config$() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
import { ISystemConfigRepository } from '@app/domain';
 | 
			
		||||
import { ISystemConfigRepository, SystemConfigCore } from '@app/domain';
 | 
			
		||||
 | 
			
		||||
export const newSystemConfigRepositoryMock = (reset = true): jest.Mocked<ISystemConfigRepository> => {
 | 
			
		||||
  if (reset) {
 | 
			
		||||
    SystemConfigCore.reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
export const newSystemConfigRepositoryMock = (): jest.Mocked<ISystemConfigRepository> => {
 | 
			
		||||
  return {
 | 
			
		||||
    load: jest.fn().mockResolvedValue([]),
 | 
			
		||||
    readFile: jest.fn(),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user