mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:39:37 -05: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:
		
							parent
							
								
									66ccf298ba
								
							
						
					
					
						commit
						0243570c0b
					
				@ -77,7 +77,7 @@ export class AssetService {
 | 
				
			|||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.access = new AccessCore(accessRepository);
 | 
					    this.access = new AccessCore(accessRepository);
 | 
				
			||||||
    this.storageCore = new StorageCore(storageRepository);
 | 
					    this.storageCore = new StorageCore(storageRepository);
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  canUploadFile({ authUser, fieldName, file }: UploadRequest): true {
 | 
					  canUploadFile({ authUser, fieldName, file }: UploadRequest): true {
 | 
				
			||||||
 | 
				
			|||||||
@ -71,7 +71,7 @@ export class AuthService {
 | 
				
			|||||||
    @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
 | 
					    @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository,
 | 
				
			||||||
    @Inject(IKeyRepository) private keyRepository: IKeyRepository,
 | 
					    @Inject(IKeyRepository) private keyRepository: IKeyRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
    this.userCore = new UserCore(userRepository, libraryRepository, cryptoRepository);
 | 
					    this.userCore = new UserCore(userRepository, libraryRepository, cryptoRepository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    custom.setHttpOptionsDefaults({ timeout: 30000 });
 | 
					    custom.setHttpOptionsDefaults({ timeout: 30000 });
 | 
				
			||||||
 | 
				
			|||||||
@ -223,8 +223,7 @@ describe(JobService.name, () => {
 | 
				
			|||||||
    it('should subscribe to config changes', async () => {
 | 
					    it('should subscribe to config changes', async () => {
 | 
				
			||||||
      await sut.registerHandlers(makeMockHandlers(false));
 | 
					      await sut.registerHandlers(makeMockHandlers(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const configCore = new SystemConfigCore(newSystemConfigRepositoryMock());
 | 
					      SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({
 | 
				
			||||||
      configCore.config$.next({
 | 
					 | 
				
			||||||
        job: {
 | 
					        job: {
 | 
				
			||||||
          [QueueName.BACKGROUND_TASK]: { concurrency: 10 },
 | 
					          [QueueName.BACKGROUND_TASK]: { concurrency: 10 },
 | 
				
			||||||
          [QueueName.CLIP_ENCODING]: { concurrency: 10 },
 | 
					          [QueueName.CLIP_ENCODING]: { concurrency: 10 },
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ export class JobService {
 | 
				
			|||||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
					    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
				
			||||||
    @Inject(IPersonRepository) private personRepository: IPersonRepository,
 | 
					    @Inject(IPersonRepository) private personRepository: IPersonRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async handleCommand(queueName: QueueName, dto: JobCommandDto): Promise<JobStatusDto> {
 | 
					  async handleCommand(queueName: QueueName, dto: JobCommandDto): Promise<JobStatusDto> {
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ export class MediaService {
 | 
				
			|||||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
					    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
				
			||||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
					    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
    this.storageCore = new StorageCore(this.storageRepository);
 | 
					    this.storageCore = new StorageCore(this.storageRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -67,7 +67,7 @@ export class MetadataService {
 | 
				
			|||||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
					    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.storageCore = new StorageCore(storageRepository);
 | 
					    this.storageCore = new StorageCore(storageRepository);
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
    this.configCore.config$.subscribe(() => this.init());
 | 
					    this.configCore.config$.subscribe(() => this.init());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,7 @@ export class PersonService {
 | 
				
			|||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.access = new AccessCore(accessRepository);
 | 
					    this.access = new AccessCore(accessRepository);
 | 
				
			||||||
    this.storageCore = new StorageCore(storageRepository);
 | 
					    this.storageCore = new StorageCore(storageRepository);
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getAll(authUser: AuthUserDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
 | 
					  async getAll(authUser: AuthUserDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,7 @@ export class SearchService {
 | 
				
			|||||||
    @Inject(ISearchRepository) private searchRepository: ISearchRepository,
 | 
					    @Inject(ISearchRepository) private searchRepository: ISearchRepository,
 | 
				
			||||||
    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
					    @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  teardown() {
 | 
					  teardown() {
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ export class ServerInfoService {
 | 
				
			|||||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
					    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
				
			||||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
					    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
    this.storageCore = new StorageCore(storageRepository);
 | 
					    this.storageCore = new StorageCore(storageRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ export class SmartInfoService {
 | 
				
			|||||||
    @Inject(ISmartInfoRepository) private repository: ISmartInfoRepository,
 | 
					    @Inject(ISmartInfoRepository) private repository: ISmartInfoRepository,
 | 
				
			||||||
    @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository,
 | 
					    @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.configCore = new SystemConfigCore(configRepository);
 | 
					    this.configCore = SystemConfigCore.create(configRepository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async handleQueueObjectTagging({ force }: IBaseJob) {
 | 
					  async handleQueueObjectTagging({ force }: IBaseJob) {
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ export class StorageTemplateService {
 | 
				
			|||||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
					    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.storageTemplate = this.compile(config.storageTemplate.template);
 | 
					    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.addValidator((config) => this.validate(config));
 | 
				
			||||||
    this.configCore.config$.subscribe((config) => this.onConfig(config));
 | 
					    this.configCore.config$.subscribe((config) => this.onConfig(config));
 | 
				
			||||||
    this.storageCore = new StorageCore(storageRepository);
 | 
					    this.storageCore = new StorageCore(storageRepository);
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,7 @@ export enum FeatureFlag {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type FeatureFlags = Record<FeatureFlag, boolean>;
 | 
					export type FeatureFlags = Record<FeatureFlag, boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const singleton = new Subject<SystemConfig>();
 | 
					let instance: SystemConfigCore | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class SystemConfigCore {
 | 
					export class SystemConfigCore {
 | 
				
			||||||
@ -142,9 +142,20 @@ export class SystemConfigCore {
 | 
				
			|||||||
  private validators: SystemConfigValidator[] = [];
 | 
					  private validators: SystemConfigValidator[] = [];
 | 
				
			||||||
  private configCache: SystemConfig | null = null;
 | 
					  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) {
 | 
					  async requireFeature(feature: FeatureFlag) {
 | 
				
			||||||
    const hasFeature = await this.hasFeature(feature);
 | 
					    const hasFeature = await this.hasFeature(feature);
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ import {
 | 
				
			|||||||
} from '@app/infra/entities';
 | 
					} from '@app/infra/entities';
 | 
				
			||||||
import { BadRequestException } from '@nestjs/common';
 | 
					import { BadRequestException } from '@nestjs/common';
 | 
				
			||||||
import { newCommunicationRepositoryMock, newJobRepositoryMock, newSystemConfigRepositoryMock } from '@test';
 | 
					import { newCommunicationRepositoryMock, newJobRepositoryMock, newSystemConfigRepositoryMock } from '@test';
 | 
				
			||||||
import { ICommunicationRepository } from '..';
 | 
					import { ICommunicationRepository } from '../communication';
 | 
				
			||||||
import { IJobRepository, JobName, QueueName } from '../job';
 | 
					import { IJobRepository, JobName, QueueName } from '../job';
 | 
				
			||||||
import { SystemConfigValidator, defaults } from './system-config.core';
 | 
					import { SystemConfigValidator, defaults } from './system-config.core';
 | 
				
			||||||
import { ISystemConfigRepository } from './system-config.repository';
 | 
					import { ISystemConfigRepository } from './system-config.repository';
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ export class SystemConfigService {
 | 
				
			|||||||
    @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository,
 | 
					    @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository,
 | 
				
			||||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
					    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.core = new SystemConfigCore(repository);
 | 
					    this.core = SystemConfigCore.create(repository);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  get config$() {
 | 
					  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 {
 | 
					  return {
 | 
				
			||||||
    load: jest.fn().mockResolvedValue([]),
 | 
					    load: jest.fn().mockResolvedValue([]),
 | 
				
			||||||
    readFile: jest.fn(),
 | 
					    readFile: jest.fn(),
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user