From 6e93ddf2f183bb201f972cc5b78773d4130e91bf Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Fri, 22 Mar 2024 18:24:02 -0400 Subject: [PATCH 01/21] refactor: server events (#8204) * refactor: server events * fix typo --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> --- server/src/app.module.ts | 6 +- server/src/decorators.ts | 5 +- ...cation.interface.ts => event.interface.ts} | 60 +++++++++++------- ...tion.repository.ts => event.repository.ts} | 63 ++++++------------- server/src/services/asset.service.spec.ts | 12 ++-- server/src/services/asset.service.ts | 12 ++-- server/src/services/job.service.spec.ts | 10 +-- server/src/services/job.service.ts | 12 ++-- server/src/services/library.service.spec.ts | 6 +- server/src/services/library.service.ts | 8 +-- server/src/services/metadata.service.spec.ts | 12 ++-- server/src/services/metadata.service.ts | 6 +- .../src/services/server-info.service.spec.ts | 17 ++--- server/src/services/server-info.service.ts | 17 ++--- .../services/storage-template.service.spec.ts | 6 +- .../src/services/storage-template.service.ts | 8 +-- .../services/system-config.service.spec.ts | 14 ++--- server/src/services/system-config.service.ts | 31 ++++----- server/src/services/trash.service.spec.ts | 14 ++--- server/src/services/trash.service.ts | 6 +- .../communication.repository.mock.ts | 12 ---- .../repositories/event.repository.mock.ts | 10 +++ 22 files changed, 166 insertions(+), 181 deletions(-) rename server/src/interfaces/{communication.interface.ts => event.interface.ts} (51%) rename server/src/repositories/{communication.repository.ts => event.repository.ts} (53%) delete mode 100644 server/test/repositories/communication.repository.mock.ts create mode 100644 server/test/repositories/event.repository.mock.ts diff --git a/server/src/app.module.ts b/server/src/app.module.ts index c44b18727..19d888936 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -43,9 +43,9 @@ import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; import { IAssetRepositoryV1 } from 'src/interfaces/asset-v1.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAuditRepository } from 'src/interfaces/audit.interface'; -import { ICommunicationRepository } from 'src/interfaces/communication.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; +import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; @@ -74,9 +74,9 @@ import { AssetStackRepository } from 'src/repositories/asset-stack.repository'; import { AssetRepositoryV1 } from 'src/repositories/asset-v1.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; import { AuditRepository } from 'src/repositories/audit.repository'; -import { CommunicationRepository } from 'src/repositories/communication.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { DatabaseRepository } from 'src/repositories/database.repository'; +import { EventRepository } from 'src/repositories/event.repository'; import { FilesystemProvider } from 'src/repositories/filesystem.provider'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; @@ -200,9 +200,9 @@ const repositories: Provider[] = [ { provide: IAssetRepositoryV1, useClass: AssetRepositoryV1 }, { provide: IAssetStackRepository, useClass: AssetStackRepository }, { provide: IAuditRepository, useClass: AuditRepository }, - { provide: ICommunicationRepository, useClass: CommunicationRepository }, { provide: ICryptoRepository, useClass: CryptoRepository }, { provide: IDatabaseRepository, useClass: DatabaseRepository }, + { provide: IEventRepository, useClass: EventRepository }, { provide: IJobRepository, useClass: JobRepository }, { provide: ILibraryRepository, useClass: LibraryRepository }, { provide: IKeyRepository, useClass: ApiKeyRepository }, diff --git a/server/src/decorators.ts b/server/src/decorators.ts index b913fe00f..39da2aa2a 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -1,7 +1,8 @@ import { SetMetadata } from '@nestjs/common'; -import { OnEvent, OnEventType } from '@nestjs/event-emitter'; +import { OnEvent } from '@nestjs/event-emitter'; import { OnEventOptions } from '@nestjs/event-emitter/dist/interfaces'; import _ from 'lodash'; +import { ServerAsyncEvent, ServerEvent } from 'src/interfaces/event.interface'; import { setUnion } from 'src/utils/set'; // PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the @@ -125,5 +126,5 @@ export interface GenerateSqlQueries { /** Decorator to enable versioning/tracking of generated Sql */ export const GenerateSql = (...options: GenerateSqlQueries[]) => SetMetadata(GENERATE_SQL_KEY, options); -export const OnEventInternal = (event: OnEventType, options?: OnEventOptions) => +export const OnServerEvent = (event: ServerEvent | ServerAsyncEvent, options?: OnEventOptions) => OnEvent(event, { suppressErrors: false, ...options }); diff --git a/server/src/interfaces/communication.interface.ts b/server/src/interfaces/event.interface.ts similarity index 51% rename from server/src/interfaces/communication.interface.ts rename to server/src/interfaces/event.interface.ts index 4627e5265..49b5177f3 100644 --- a/server/src/interfaces/communication.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -2,7 +2,7 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto'; import { SystemConfig } from 'src/entities/system-config.entity'; -export const ICommunicationRepository = 'ICommunicationRepository'; +export const IEventRepository = 'IEventRepository'; export enum ClientEvent { UPLOAD_SUCCESS = 'on_upload_success', @@ -19,18 +19,6 @@ export enum ClientEvent { NEW_RELEASE = 'on_new_release', } -export enum ServerEvent { - CONFIG_UPDATE = 'config:update', -} - -export enum InternalEvent { - VALIDATE_CONFIG = 'validate_config', -} - -export interface InternalEventMap { - [InternalEvent.VALIDATE_CONFIG]: { newConfig: SystemConfig; oldConfig: SystemConfig }; -} - export interface ClientEventMap { [ClientEvent.UPLOAD_SUCCESS]: AssetResponseDto; [ClientEvent.USER_DELETE]: string; @@ -46,15 +34,39 @@ export interface ClientEventMap { [ClientEvent.NEW_RELEASE]: ReleaseNotification; } -export type OnConnectCallback = (userId: string) => void | Promise; -export type OnServerEventCallback = () => Promise; - -export interface ICommunicationRepository { - send(event: E, userId: string, data: ClientEventMap[E]): void; - broadcast(event: E, data: ClientEventMap[E]): void; - on(event: 'connect', callback: OnConnectCallback): void; - on(event: ServerEvent, callback: OnServerEventCallback): void; - sendServerEvent(event: ServerEvent): void; - emit(event: E, data: InternalEventMap[E]): boolean; - emitAsync(event: E, data: InternalEventMap[E]): Promise; +export enum ServerEvent { + CONFIG_UPDATE = 'config.update', + WEBSOCKET_CONNECT = 'websocket.connect', +} + +export interface ServerEventMap { + [ServerEvent.CONFIG_UPDATE]: null; + [ServerEvent.WEBSOCKET_CONNECT]: { userId: string }; +} + +export enum ServerAsyncEvent { + CONFIG_VALIDATE = 'config.validate', +} + +export interface ServerAsyncEventMap { + [ServerAsyncEvent.CONFIG_VALIDATE]: { newConfig: SystemConfig; oldConfig: SystemConfig }; +} + +export interface IEventRepository { + /** + * Send to connected clients for a specific user + */ + clientSend(event: E, userId: string, data: ClientEventMap[E]): void; + /** + * Send to all connected clients + */ + clientBroadcast(event: E, data: ClientEventMap[E]): void; + /** + * Notify listeners in this and connected processes. Subscribe to an event with `@OnServerEvent` + */ + serverSend(event: E, data: ServerEventMap[E]): boolean; + /** + * Notify and wait for responses from listeners in this process. Subscribe to an event with `@OnServerEvent` + */ + serverSendAsync(event: E, data: ServerAsyncEventMap[E]): Promise; } diff --git a/server/src/repositories/communication.repository.ts b/server/src/repositories/event.repository.ts similarity index 53% rename from server/src/repositories/communication.repository.ts rename to server/src/repositories/event.repository.ts index 046190e1a..be1de76c2 100644 --- a/server/src/repositories/communication.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -8,13 +8,12 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { - ClientEvent, - ICommunicationRepository, - InternalEventMap, - OnConnectCallback, - OnServerEventCallback, + ClientEventMap, + IEventRepository, + ServerAsyncEventMap, ServerEvent, -} from 'src/interfaces/communication.interface'; + ServerEventMap, +} from 'src/interfaces/event.interface'; import { AuthService } from 'src/services/auth.service'; import { Instrumentation } from 'src/utils/instrumentation'; import { ImmichLogger } from 'src/utils/logger'; @@ -25,14 +24,8 @@ import { ImmichLogger } from 'src/utils/logger'; path: '/api/socket.io', transports: ['websocket'], }) -export class CommunicationRepository - implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, ICommunicationRepository -{ - private logger = new ImmichLogger(CommunicationRepository.name); - private onConnectCallbacks: OnConnectCallback[] = []; - private onServerEventCallbacks: Record = { - [ServerEvent.CONFIG_UPDATE]: [], - }; +export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, IEventRepository { + private logger = new ImmichLogger(EventRepository.name); @WebSocketServer() private server?: Server; @@ -46,38 +39,23 @@ export class CommunicationRepository this.logger.log('Initialized websocket server'); for (const event of Object.values(ServerEvent)) { - server.on(event, async () => { + if (event === ServerEvent.WEBSOCKET_CONNECT) { + continue; + } + + server.on(event, (data: unknown) => { this.logger.debug(`Server event: ${event} (receive)`); - const callbacks = this.onServerEventCallbacks[event]; - for (const callback of callbacks) { - await callback(); - } + this.eventEmitter.emit(event, data); }); } } - on(event: 'connect' | ServerEvent, callback: OnConnectCallback | OnServerEventCallback) { - switch (event) { - case 'connect': { - this.onConnectCallbacks.push(callback); - break; - } - - default: { - this.onServerEventCallbacks[event].push(callback as OnServerEventCallback); - break; - } - } - } - async handleConnection(client: Socket) { try { this.logger.log(`Websocket Connect: ${client.id}`); const auth = await this.authService.validate(client.request.headers, {}); await client.join(auth.user.id); - for (const callback of this.onConnectCallbacks) { - await callback(auth.user.id); - } + this.serverSend(ServerEvent.WEBSOCKET_CONNECT, { userId: auth.user.id }); } catch (error: Error | any) { this.logger.error(`Websocket connection error: ${error}`, error?.stack); client.emit('error', 'unauthorized'); @@ -90,24 +68,21 @@ export class CommunicationRepository await client.leave(client.nsp.name); } - send(event: ClientEvent, userId: string, data: any) { + clientSend(event: E, userId: string, data: ClientEventMap[E]) { this.server?.to(userId).emit(event, data); } - broadcast(event: ClientEvent, data: any) { + clientBroadcast(event: E, data: ClientEventMap[E]) { this.server?.emit(event, data); } - sendServerEvent(event: ServerEvent) { + serverSend(event: E, data: ServerEventMap[E]) { this.logger.debug(`Server event: ${event} (send)`); - this.server?.serverSideEmit(event); - } - - emit(event: E, data: InternalEventMap[E]): boolean { + this.server?.serverSideEmit(event, data); return this.eventEmitter.emit(event, data); } - emitAsync(event: E, data: InternalEventMap[E]): Promise { + serverSendAsync(event: E, data: ServerAsyncEventMap[E]): Promise { return this.eventEmitter.emitAsync(event, data) as Promise; } } diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index a8e30a388..df1f819b4 100644 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -5,7 +5,7 @@ import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/a import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; import { AssetStats, IAssetRepository, TimeBucketSize } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobItem, JobName } from 'src/interfaces/job.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; @@ -20,7 +20,7 @@ import { userStub } from 'test/fixtures/user.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetStackRepositoryMock } from 'test/repositories/asset-stack.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; @@ -152,7 +152,7 @@ describe(AssetService.name, () => { let jobMock: jest.Mocked; let storageMock: jest.Mocked; let userMock: jest.Mocked; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; let configMock: jest.Mocked; let partnerMock: jest.Mocked; let assetStackMock: jest.Mocked; @@ -164,7 +164,7 @@ describe(AssetService.name, () => { beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); + eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); @@ -179,7 +179,7 @@ describe(AssetService.name, () => { configMock, storageMock, userMock, - communicationMock, + eventMock, partnerMock, assetStackMock, ); @@ -704,7 +704,7 @@ describe(AssetService.name, () => { stackParentId: 'parent', }); - expect(communicationMock.send).toHaveBeenCalledWith(ClientEvent.ASSET_STACK_UPDATE, authStub.user1.user.id, [ + expect(eventMock.clientSend).toHaveBeenCalledWith(ClientEvent.ASSET_STACK_UPDATE, authStub.user1.user.id, [ 'asset-1', 'parent', ]); diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 7020d5061..230415e80 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -31,7 +31,7 @@ import { LibraryType } from 'src/entities/library.entity'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IAssetDeletionJob, IJobRepository, @@ -75,7 +75,7 @@ export class AssetService { @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IUserRepository) private userRepository: IUserRepository, - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, @Inject(IAssetStackRepository) private assetStackRepository: IAssetStackRepository, ) { @@ -395,7 +395,7 @@ export class AssetService { .flatMap((stack) => (stack ? [stack] : [])) .filter((stack) => stack.assets.length < 2); await Promise.all(stacksToDelete.map((as) => this.assetStackRepository.delete(as.id))); - this.communicationRepository.send(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, ids); + this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, ids); } async handleAssetDeletionCheck(): Promise { @@ -454,7 +454,7 @@ export class AssetService { await this.assetRepository.remove(asset); await this.userRepository.updateUsage(asset.ownerId, -(asset.exifInfo?.fileSizeInByte || 0)); - this.communicationRepository.send(ClientEvent.ASSET_DELETE, asset.ownerId, id); + this.eventRepository.clientSend(ClientEvent.ASSET_DELETE, asset.ownerId, id); // TODO refactor this to use cascades if (asset.livePhotoVideoId) { @@ -482,7 +482,7 @@ export class AssetService { await this.jobRepository.queueAll(ids.map((id) => ({ name: JobName.ASSET_DELETION, data: { id } }))); } else { await this.assetRepository.softDeleteAll(ids); - this.communicationRepository.send(ClientEvent.ASSET_TRASH, auth.user.id, ids); + this.eventRepository.clientSend(ClientEvent.ASSET_TRASH, auth.user.id, ids); } } @@ -513,7 +513,7 @@ export class AssetService { primaryAssetId: newParentId, }); - this.communicationRepository.send(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, [ + this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, [ ...childIds, newParentId, oldParentId, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index b680abaf1..d56a9237e 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -2,7 +2,7 @@ import { BadRequestException } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobCommand, @@ -17,7 +17,7 @@ import { ISystemConfigRepository } from 'src/interfaces/system-config.interface' import { JobService } from 'src/services/job.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; @@ -34,17 +34,17 @@ describe(JobService.name, () => { let sut: JobService; let assetMock: jest.Mocked; let configMock: jest.Mocked; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; let jobMock: jest.Mocked; let personMock: jest.Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); configMock = newSystemConfigRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); + eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); personMock = newPersonRepositoryMock(); - sut = new JobService(assetMock, communicationMock, jobMock, configMock, personMock); + sut = new JobService(assetMock, eventMock, jobMock, configMock, personMock); }); it('should work', () => { diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index e6d9b0781..d11977ebf 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -4,7 +4,7 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto } from 'src/dtos/job.dto'; import { AssetType } from 'src/entities/asset.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { ConcurrentQueueName, IJobRepository, @@ -27,7 +27,7 @@ export class JobService { constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IPersonRepository) private personRepository: IPersonRepository, @@ -219,7 +219,7 @@ export class JobService { if (item.data.source === 'sidecar-write') { const [asset] = await this.assetRepository.getByIdsWithAllRelations([item.data.id]); if (asset) { - this.communicationRepository.send(ClientEvent.ASSET_UPDATE, asset.ownerId, mapAsset(asset)); + this.eventRepository.clientSend(ClientEvent.ASSET_UPDATE, asset.ownerId, mapAsset(asset)); } } await this.jobRepository.queue({ name: JobName.LINK_LIVE_PHOTOS, data: item.data }); @@ -242,7 +242,7 @@ export class JobService { const { id } = item.data; const person = await this.personRepository.getById(id); if (person) { - this.communicationRepository.send(ClientEvent.PERSON_THUMBNAIL, person.ownerId, person.id); + this.eventRepository.clientSend(ClientEvent.PERSON_THUMBNAIL, person.ownerId, person.id); } break; } @@ -279,13 +279,13 @@ export class JobService { // Only live-photo motion part will be marked as not visible immediately on upload. Skip notifying clients if (asset && asset.isVisible) { - this.communicationRepository.send(ClientEvent.UPLOAD_SUCCESS, asset.ownerId, mapAsset(asset)); + this.eventRepository.clientSend(ClientEvent.UPLOAD_SUCCESS, asset.ownerId, mapAsset(asset)); } break; } case JobName.USER_DELETION: { - this.communicationRepository.broadcast(ClientEvent.USER_DELETE, item.data.id); + this.eventRepository.clientBroadcast(ClientEvent.USER_DELETE, item.data.id); break; } } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 663f8ff68..1af419aff 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -127,10 +127,10 @@ describe(LibraryService.name, () => { }); }); - describe('validateConfig', () => { + describe('onValidateConfig', () => { it('should allow a valid cron expression', () => { expect(() => - sut.validateConfig({ + sut.onValidateConfig({ newConfig: { library: { scan: { cronExpression: '0 0 * * *' } } } as SystemConfig, oldConfig: {} as SystemConfig, }), @@ -139,7 +139,7 @@ describe(LibraryService.name, () => { it('should fail for an invalid cron expression', () => { expect(() => - sut.validateConfig({ + sut.onValidateConfig({ newConfig: { library: { scan: { cronExpression: 'foo' } } } as SystemConfig, oldConfig: {} as SystemConfig, }), diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index d46d40edf..b3c491535 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -7,7 +7,7 @@ import path, { basename, parse } from 'node:path'; import picomatch from 'picomatch'; import { StorageCore } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { OnEventInternal } from 'src/decorators'; +import { OnServerEvent } from 'src/decorators'; import { CreateLibraryDto, LibraryResponseDto, @@ -23,9 +23,9 @@ import { import { AssetType } from 'src/entities/asset.entity'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; import { IAssetRepository, WithProperty } from 'src/interfaces/asset.interface'; -import { InternalEvent, InternalEventMap } from 'src/interfaces/communication.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; +import { ServerAsyncEvent, ServerAsyncEventMap } from 'src/interfaces/event.interface'; import { IBaseJob, IEntityJob, @@ -105,8 +105,8 @@ export class LibraryService extends EventEmitter { }); } - @OnEventInternal(InternalEvent.VALIDATE_CONFIG) - validateConfig({ newConfig }: InternalEventMap[InternalEvent.VALIDATE_CONFIG]) { + @OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE) + onValidateConfig({ newConfig }: ServerAsyncEventMap[ServerAsyncEvent.CONFIG_VALIDATE]) { const { scan } = newConfig.library; if (!validateCronExpression(scan.cronExpression)) { throw new Error(`Invalid cron expression ${scan.cronExpression}`); diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 4dafa0ac5..fd2c3e388 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -8,9 +8,9 @@ import { ExifEntity } from 'src/entities/exif.entity'; import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; @@ -24,9 +24,9 @@ import { fileStub } from 'test/fixtures/file.stub'; import { probeStub } from 'test/fixtures/media.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock'; @@ -46,7 +46,7 @@ describe(MetadataService.name, () => { let mediaMock: jest.Mocked; let personMock: jest.Mocked; let storageMock: jest.Mocked; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; let databaseMock: jest.Mocked; let sut: MetadataService; @@ -59,7 +59,7 @@ describe(MetadataService.name, () => { metadataMock = newMetadataRepositoryMock(); moveMock = newMoveRepositoryMock(); personMock = newPersonRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); + eventMock = newEventRepositoryMock(); storageMock = newStorageRepositoryMock(); mediaMock = newMediaRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); @@ -67,7 +67,7 @@ describe(MetadataService.name, () => { sut = new MetadataService( albumMock, assetMock, - communicationMock, + eventMock, cryptoRepository, databaseMock, jobMock, @@ -195,7 +195,7 @@ describe(MetadataService.name, () => { await expect(sut.handleLivePhotoLinking({ id: assetStub.livePhotoStillAsset.id })).resolves.toBe( JobStatus.SUCCESS, ); - expect(communicationMock.send).toHaveBeenCalledWith( + expect(eventMock.clientSend).toHaveBeenCalledWith( ClientEvent.ASSET_HIDDEN, assetStub.livePhotoMotionAsset.ownerId, assetStub.livePhotoMotionAsset.id, diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 849a12da9..9b249a49f 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -12,9 +12,9 @@ import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IBaseJob, IEntityJob, @@ -105,7 +105,7 @@ export class MetadataService { constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @@ -185,7 +185,7 @@ export class MetadataService { await this.albumRepository.removeAsset(motionAsset.id); // Notify clients to hide the linked live photo asset - this.communicationRepository.send(ClientEvent.ASSET_HIDDEN, motionAsset.ownerId, motionAsset.id); + this.eventRepository.clientSend(ClientEvent.ASSET_HIDDEN, motionAsset.ownerId, motionAsset.id); return JobStatus.SUCCESS; } diff --git a/server/src/services/server-info.service.spec.ts b/server/src/services/server-info.service.spec.ts index bbb608b21..0348f26d2 100644 --- a/server/src/services/server-info.service.spec.ts +++ b/server/src/services/server-info.service.spec.ts @@ -1,13 +1,13 @@ import { serverVersion } from 'src/constants'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; -import { ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { IEventRepository } from 'src/interfaces/event.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { ServerInfoService } from 'src/services/server-info.service'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newServerInfoRepositoryMock } from 'test/repositories/system-info.repository.mock'; @@ -16,7 +16,7 @@ import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; describe(ServerInfoService.name, () => { let sut: ServerInfoService; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; let configMock: jest.Mocked; let serverInfoMock: jest.Mocked; let storageMock: jest.Mocked; @@ -25,20 +25,13 @@ describe(ServerInfoService.name, () => { beforeEach(() => { configMock = newSystemConfigRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); + eventMock = newEventRepositoryMock(); serverInfoMock = newServerInfoRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); systemMetadataMock = newSystemMetadataRepositoryMock(); - sut = new ServerInfoService( - communicationMock, - configMock, - userMock, - serverInfoMock, - storageMock, - systemMetadataMock, - ); + sut = new ServerInfoService(eventMock, configMock, userMock, serverInfoMock, storageMock, systemMetadataMock); }); it('should work', () => { diff --git a/server/src/services/server-info.service.ts b/server/src/services/server-info.service.ts index 94195fd4a..9f0c1e290 100644 --- a/server/src/services/server-info.service.ts +++ b/server/src/services/server-info.service.ts @@ -3,6 +3,7 @@ import { DateTime } from 'luxon'; import { isDev, serverVersion } from 'src/constants'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; +import { OnServerEvent } from 'src/decorators'; import { ServerConfigDto, ServerFeaturesDto, @@ -13,7 +14,7 @@ import { UsageByUserDto, } from 'src/dtos/server-info.dto'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository, ServerEvent, ServerEventMap } from 'src/interfaces/event.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; @@ -32,7 +33,7 @@ export class ServerInfoService { private releaseVersionCheckedAt: DateTime | null = null; constructor( - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IServerInfoRepository) private repository: IServerInfoRepository, @@ -40,9 +41,10 @@ export class ServerInfoService { @Inject(ISystemMetadataRepository) private readonly systemMetadataRepository: ISystemMetadataRepository, ) { this.configCore = SystemConfigCore.create(configRepository); - this.communicationRepository.on('connect', (userId) => this.handleConnect(userId)); } + onConnect() {} + async init(): Promise { await this.handleVersionCheck(); @@ -169,8 +171,9 @@ export class ServerInfoService { return true; } - private handleConnect(userId: string) { - this.communicationRepository.send(ClientEvent.SERVER_VERSION, userId, serverVersion); + @OnServerEvent(ServerEvent.WEBSOCKET_CONNECT) + onWebsocketConnection({ userId }: ServerEventMap[ServerEvent.WEBSOCKET_CONNECT]) { + this.eventRepository.clientSend(ClientEvent.SERVER_VERSION, userId, serverVersion); this.newReleaseNotification(userId); } @@ -184,7 +187,7 @@ export class ServerInfoService { }; userId - ? this.communicationRepository.send(event, userId, payload) - : this.communicationRepository.broadcast(event, payload); + ? this.eventRepository.clientSend(event, userId, payload) + : this.eventRepository.clientBroadcast(event, payload); } } diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 1254705ae..ba1cb3e59 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -70,10 +70,10 @@ describe(StorageTemplateService.name, () => { SystemConfigCore.create(configMock).config$.next(defaults); }); - describe('validate', () => { + describe('onValidateConfig', () => { it('should allow valid templates', () => { expect(() => - sut.validate({ + sut.onValidateConfig({ newConfig: { storageTemplate: { template: @@ -87,7 +87,7 @@ describe(StorageTemplateService.name, () => { it('should fail for an invalid template', () => { expect(() => - sut.validate({ + sut.onValidateConfig({ newConfig: { storageTemplate: { template: '{{foo}}', diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 39a0196f2..280c37b95 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -14,15 +14,15 @@ import { } from 'src/constants'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { OnEventInternal } from 'src/decorators'; +import { OnServerEvent } from 'src/decorators'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; import { SystemConfig } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { InternalEvent, InternalEventMap } from 'src/interfaces/communication.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; +import { ServerAsyncEvent, ServerAsyncEventMap } from 'src/interfaces/event.interface'; import { IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; @@ -86,8 +86,8 @@ export class StorageTemplateService { ); } - @OnEventInternal(InternalEvent.VALIDATE_CONFIG) - validate({ newConfig }: InternalEventMap[InternalEvent.VALIDATE_CONFIG]) { + @OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE) + onValidateConfig({ newConfig }: ServerAsyncEventMap[ServerAsyncEvent.CONFIG_VALIDATE]) { try { const { compiled } = this.compile(newConfig.storageTemplate.template); this.render(compiled, { diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 122708a63..b2079f606 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -13,13 +13,13 @@ import { TranscodePolicy, VideoCodec, } from 'src/entities/system-config.entity'; -import { ICommunicationRepository, ServerEvent } from 'src/interfaces/communication.interface'; +import { IEventRepository, ServerEvent } from 'src/interfaces/event.interface'; import { QueueName } from 'src/interfaces/job.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { SystemConfigService } from 'src/services/system-config.service'; import { ImmichLogger } from 'src/utils/logger'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; const updates: SystemConfigEntity[] = [ @@ -152,14 +152,14 @@ const updatedConfig = Object.freeze({ describe(SystemConfigService.name, () => { let sut: SystemConfigService; let configMock: jest.Mocked; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; let smartInfoMock: jest.Mocked; beforeEach(() => { delete process.env.IMMICH_CONFIG_FILE; configMock = newSystemConfigRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); - sut = new SystemConfigService(configMock, communicationMock, smartInfoMock); + eventMock = newEventRepositoryMock(); + sut = new SystemConfigService(configMock, eventMock, smartInfoMock); }); it('should work', () => { @@ -330,8 +330,8 @@ describe(SystemConfigService.name, () => { await expect(sut.updateConfig(updatedConfig)).resolves.toEqual(updatedConfig); - expect(communicationMock.broadcast).toHaveBeenCalled(); - expect(communicationMock.sendServerEvent).toHaveBeenCalledWith(ServerEvent.CONFIG_UPDATE); + expect(eventMock.clientBroadcast).toHaveBeenCalled(); + expect(eventMock.serverSend).toHaveBeenCalledWith(ServerEvent.CONFIG_UPDATE, null); expect(configMock.saveAll).toHaveBeenCalledWith(updates); }); diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index 5842342db..bc57c83ac 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -12,16 +12,16 @@ import { supportedYearTokens, } from 'src/constants'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { OnEventInternal } from 'src/decorators'; +import { OnServerEvent } from 'src/decorators'; import { SystemConfigDto, SystemConfigTemplateStorageOptionDto, mapConfig } from 'src/dtos/system-config.dto'; import { LogLevel, SystemConfig } from 'src/entities/system-config.entity'; import { ClientEvent, - ICommunicationRepository, - InternalEvent, - InternalEventMap, + IEventRepository, + ServerAsyncEvent, + ServerAsyncEventMap, ServerEvent, -} from 'src/interfaces/communication.interface'; +} from 'src/interfaces/event.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { ImmichLogger } from 'src/utils/logger'; @@ -33,11 +33,10 @@ export class SystemConfigService { constructor( @Inject(ISystemConfigRepository) private repository: ISystemConfigRepository, - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(ISearchRepository) private smartInfoRepository: ISearchRepository, ) { this.core = SystemConfigCore.create(repository); - this.communicationRepository.on(ServerEvent.CONFIG_UPDATE, () => this.handleConfigUpdate()); this.core.config$.subscribe((config) => this.setLogLevel(config)); } @@ -60,8 +59,8 @@ export class SystemConfigService { return mapConfig(config); } - @OnEventInternal(InternalEvent.VALIDATE_CONFIG) - validateConfig({ newConfig, oldConfig }: InternalEventMap[InternalEvent.VALIDATE_CONFIG]) { + @OnServerEvent(ServerAsyncEvent.CONFIG_VALIDATE) + onValidateConfig({ newConfig, oldConfig }: ServerAsyncEventMap[ServerAsyncEvent.CONFIG_VALIDATE]) { if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && this.getEnvLogLevel()) { throw new Error('Logging cannot be changed while the environment variable LOG_LEVEL is set.'); } @@ -71,7 +70,10 @@ export class SystemConfigService { const oldConfig = await this.core.getConfig(); try { - await this.communicationRepository.emitAsync(InternalEvent.VALIDATE_CONFIG, { newConfig: dto, oldConfig }); + await this.eventRepository.serverSendAsync(ServerAsyncEvent.CONFIG_VALIDATE, { + newConfig: dto, + oldConfig, + }); } catch (error) { this.logger.warn(`Unable to save system config due to a validation error: ${error}`); throw new BadRequestException(error instanceof Error ? error.message : error); @@ -79,8 +81,8 @@ export class SystemConfigService { const newConfig = await this.core.updateConfig(dto); - this.communicationRepository.broadcast(ClientEvent.CONFIG_UPDATE, {}); - this.communicationRepository.sendServerEvent(ServerEvent.CONFIG_UPDATE); + this.eventRepository.clientBroadcast(ClientEvent.CONFIG_UPDATE, {}); + this.eventRepository.serverSend(ServerEvent.CONFIG_UPDATE, null); if (oldConfig.machineLearning.clip.modelName !== newConfig.machineLearning.clip.modelName) { await this.smartInfoRepository.init(newConfig.machineLearning.clip.modelName); @@ -90,7 +92,7 @@ export class SystemConfigService { // this is only used by the cli on config change, and it's not actually needed anymore async refreshConfig() { - this.communicationRepository.sendServerEvent(ServerEvent.CONFIG_UPDATE); + this.eventRepository.serverSend(ServerEvent.CONFIG_UPDATE, null); await this.core.refreshConfig(); return true; } @@ -126,7 +128,8 @@ export class SystemConfigService { return theme.customCss; } - private async handleConfigUpdate() { + @OnServerEvent(ServerEvent.CONFIG_UPDATE) + async onConfigUpdate() { await this.core.refreshConfig(); } diff --git a/server/src/services/trash.service.spec.ts b/server/src/services/trash.service.spec.ts index e43926e4d..ecdf577ed 100644 --- a/server/src/services/trash.service.spec.ts +++ b/server/src/services/trash.service.spec.ts @@ -1,13 +1,13 @@ import { BadRequestException } from '@nestjs/common'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { TrashService } from 'src/services/trash.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; -import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; +import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; describe(TrashService.name, () => { @@ -15,7 +15,7 @@ describe(TrashService.name, () => { let accessMock: IAccessRepositoryMock; let assetMock: jest.Mocked; let jobMock: jest.Mocked; - let communicationMock: jest.Mocked; + let eventMock: jest.Mocked; it('should work', () => { expect(sut).toBeDefined(); @@ -24,10 +24,10 @@ describe(TrashService.name, () => { beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); - communicationMock = newCommunicationRepositoryMock(); + eventMock = newEventRepositoryMock(); jobMock = newJobRepositoryMock(); - sut = new TrashService(accessMock, assetMock, jobMock, communicationMock); + sut = new TrashService(accessMock, assetMock, jobMock, eventMock); }); describe('restoreAssets', () => { @@ -54,14 +54,14 @@ describe(TrashService.name, () => { assetMock.getByUserId.mockResolvedValue({ items: [], hasNextPage: false }); await expect(sut.restore(authStub.user1)).resolves.toBeUndefined(); expect(assetMock.restoreAll).not.toHaveBeenCalled(); - expect(communicationMock.send).not.toHaveBeenCalled(); + expect(eventMock.clientSend).not.toHaveBeenCalled(); }); it('should restore and notify', async () => { assetMock.getByUserId.mockResolvedValue({ items: [assetStub.image], hasNextPage: false }); await expect(sut.restore(authStub.user1)).resolves.toBeUndefined(); expect(assetMock.restoreAll).toHaveBeenCalledWith([assetStub.image.id]); - expect(communicationMock.send).toHaveBeenCalledWith(ClientEvent.ASSET_RESTORE, authStub.user1.user.id, [ + expect(eventMock.clientSend).toHaveBeenCalledWith(ClientEvent.ASSET_RESTORE, authStub.user1.user.id, [ assetStub.image.id, ]); }); diff --git a/server/src/services/trash.service.ts b/server/src/services/trash.service.ts index 5f1ee29f7..f74ea8098 100644 --- a/server/src/services/trash.service.ts +++ b/server/src/services/trash.service.ts @@ -5,7 +5,7 @@ import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { IAccessRepository } from 'src/interfaces/access.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; -import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.interface'; +import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/interfaces/job.interface'; import { usePagination } from 'src/utils/pagination'; @@ -16,7 +16,7 @@ export class TrashService { @Inject(IAccessRepository) accessRepository: IAccessRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, - @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, + @Inject(IEventRepository) private eventRepository: IEventRepository, ) { this.access = AccessCore.create(accessRepository); } @@ -60,6 +60,6 @@ export class TrashService { } await this.assetRepository.restoreAll(ids); - this.communicationRepository.send(ClientEvent.ASSET_RESTORE, auth.user.id, ids); + this.eventRepository.clientSend(ClientEvent.ASSET_RESTORE, auth.user.id, ids); } } diff --git a/server/test/repositories/communication.repository.mock.ts b/server/test/repositories/communication.repository.mock.ts deleted file mode 100644 index 38284f377..000000000 --- a/server/test/repositories/communication.repository.mock.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ICommunicationRepository } from 'src/interfaces/communication.interface'; - -export const newCommunicationRepositoryMock = (): jest.Mocked => { - return { - send: jest.fn(), - broadcast: jest.fn(), - on: jest.fn(), - sendServerEvent: jest.fn(), - emit: jest.fn(), - emitAsync: jest.fn(), - }; -}; diff --git a/server/test/repositories/event.repository.mock.ts b/server/test/repositories/event.repository.mock.ts new file mode 100644 index 000000000..b21d4a59e --- /dev/null +++ b/server/test/repositories/event.repository.mock.ts @@ -0,0 +1,10 @@ +import { IEventRepository } from 'src/interfaces/event.interface'; + +export const newEventRepositoryMock = (): jest.Mocked => { + return { + clientSend: jest.fn(), + clientBroadcast: jest.fn(), + serverSend: jest.fn(), + serverSendAsync: jest.fn(), + }; +}; From 604b8ff17cb72693e47922da530280533853a6aa Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Sat, 23 Mar 2024 00:20:16 -0400 Subject: [PATCH 02/21] chore(server): remove getByDate from asset repo (#8211) * remove getByDate * remove unused import --- server/src/interfaces/asset.interface.ts | 1 - server/src/queries/asset.repository.sql | 76 ------------------- server/src/repositories/asset.repository.ts | 36 --------- .../repositories/asset.repository.mock.ts | 1 - 4 files changed, 114 deletions(-) diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index 47c13041f..008931566 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -139,7 +139,6 @@ export const IAssetRepository = 'IAssetRepository'; export interface IAssetRepository { create(asset: AssetCreate): Promise; - getByDate(ownerId: string, date: Date): Promise; getByIds( ids: string[], relations?: FindOptionsRelations, diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index e230a7346..a24b390b9 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -1,81 +1,5 @@ -- NOTE: This file is auto generated by ./sql-generator --- AssetRepository.getByDate -SELECT - "AssetEntity"."id" AS "AssetEntity_id", - "AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId", - "AssetEntity"."ownerId" AS "AssetEntity_ownerId", - "AssetEntity"."libraryId" AS "AssetEntity_libraryId", - "AssetEntity"."deviceId" AS "AssetEntity_deviceId", - "AssetEntity"."type" AS "AssetEntity_type", - "AssetEntity"."originalPath" AS "AssetEntity_originalPath", - "AssetEntity"."resizePath" AS "AssetEntity_resizePath", - "AssetEntity"."webpPath" AS "AssetEntity_webpPath", - "AssetEntity"."thumbhash" AS "AssetEntity_thumbhash", - "AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath", - "AssetEntity"."createdAt" AS "AssetEntity_createdAt", - "AssetEntity"."updatedAt" AS "AssetEntity_updatedAt", - "AssetEntity"."deletedAt" AS "AssetEntity_deletedAt", - "AssetEntity"."fileCreatedAt" AS "AssetEntity_fileCreatedAt", - "AssetEntity"."localDateTime" AS "AssetEntity_localDateTime", - "AssetEntity"."fileModifiedAt" AS "AssetEntity_fileModifiedAt", - "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", - "AssetEntity"."isArchived" AS "AssetEntity_isArchived", - "AssetEntity"."isExternal" AS "AssetEntity_isExternal", - "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", - "AssetEntity"."isOffline" AS "AssetEntity_isOffline", - "AssetEntity"."checksum" AS "AssetEntity_checksum", - "AssetEntity"."duration" AS "AssetEntity_duration", - "AssetEntity"."isVisible" AS "AssetEntity_isVisible", - "AssetEntity"."livePhotoVideoId" AS "AssetEntity_livePhotoVideoId", - "AssetEntity"."originalFileName" AS "AssetEntity_originalFileName", - "AssetEntity"."sidecarPath" AS "AssetEntity_sidecarPath", - "AssetEntity"."stackId" AS "AssetEntity_stackId", - "AssetEntity__AssetEntity_exifInfo"."assetId" AS "AssetEntity__AssetEntity_exifInfo_assetId", - "AssetEntity__AssetEntity_exifInfo"."description" AS "AssetEntity__AssetEntity_exifInfo_description", - "AssetEntity__AssetEntity_exifInfo"."exifImageWidth" AS "AssetEntity__AssetEntity_exifInfo_exifImageWidth", - "AssetEntity__AssetEntity_exifInfo"."exifImageHeight" AS "AssetEntity__AssetEntity_exifInfo_exifImageHeight", - "AssetEntity__AssetEntity_exifInfo"."fileSizeInByte" AS "AssetEntity__AssetEntity_exifInfo_fileSizeInByte", - "AssetEntity__AssetEntity_exifInfo"."orientation" AS "AssetEntity__AssetEntity_exifInfo_orientation", - "AssetEntity__AssetEntity_exifInfo"."dateTimeOriginal" AS "AssetEntity__AssetEntity_exifInfo_dateTimeOriginal", - "AssetEntity__AssetEntity_exifInfo"."modifyDate" AS "AssetEntity__AssetEntity_exifInfo_modifyDate", - "AssetEntity__AssetEntity_exifInfo"."timeZone" AS "AssetEntity__AssetEntity_exifInfo_timeZone", - "AssetEntity__AssetEntity_exifInfo"."latitude" AS "AssetEntity__AssetEntity_exifInfo_latitude", - "AssetEntity__AssetEntity_exifInfo"."longitude" AS "AssetEntity__AssetEntity_exifInfo_longitude", - "AssetEntity__AssetEntity_exifInfo"."projectionType" AS "AssetEntity__AssetEntity_exifInfo_projectionType", - "AssetEntity__AssetEntity_exifInfo"."city" AS "AssetEntity__AssetEntity_exifInfo_city", - "AssetEntity__AssetEntity_exifInfo"."livePhotoCID" AS "AssetEntity__AssetEntity_exifInfo_livePhotoCID", - "AssetEntity__AssetEntity_exifInfo"."autoStackId" AS "AssetEntity__AssetEntity_exifInfo_autoStackId", - "AssetEntity__AssetEntity_exifInfo"."state" AS "AssetEntity__AssetEntity_exifInfo_state", - "AssetEntity__AssetEntity_exifInfo"."country" AS "AssetEntity__AssetEntity_exifInfo_country", - "AssetEntity__AssetEntity_exifInfo"."make" AS "AssetEntity__AssetEntity_exifInfo_make", - "AssetEntity__AssetEntity_exifInfo"."model" AS "AssetEntity__AssetEntity_exifInfo_model", - "AssetEntity__AssetEntity_exifInfo"."lensModel" AS "AssetEntity__AssetEntity_exifInfo_lensModel", - "AssetEntity__AssetEntity_exifInfo"."fNumber" AS "AssetEntity__AssetEntity_exifInfo_fNumber", - "AssetEntity__AssetEntity_exifInfo"."focalLength" AS "AssetEntity__AssetEntity_exifInfo_focalLength", - "AssetEntity__AssetEntity_exifInfo"."iso" AS "AssetEntity__AssetEntity_exifInfo_iso", - "AssetEntity__AssetEntity_exifInfo"."exposureTime" AS "AssetEntity__AssetEntity_exifInfo_exposureTime", - "AssetEntity__AssetEntity_exifInfo"."profileDescription" AS "AssetEntity__AssetEntity_exifInfo_profileDescription", - "AssetEntity__AssetEntity_exifInfo"."colorspace" AS "AssetEntity__AssetEntity_exifInfo_colorspace", - "AssetEntity__AssetEntity_exifInfo"."bitsPerSample" AS "AssetEntity__AssetEntity_exifInfo_bitsPerSample", - "AssetEntity__AssetEntity_exifInfo"."fps" AS "AssetEntity__AssetEntity_exifInfo_fps" -FROM - "assets" "AssetEntity" - LEFT JOIN "exif" "AssetEntity__AssetEntity_exifInfo" ON "AssetEntity__AssetEntity_exifInfo"."assetId" = "AssetEntity"."id" -WHERE - ( - ( - ("AssetEntity"."ownerId" = $1) - AND ("AssetEntity"."isVisible" = $2) - AND ("AssetEntity"."isArchived" = $3) - AND (NOT ("AssetEntity"."resizePath" IS NULL)) - AND ("AssetEntity"."fileCreatedAt" BETWEEN $4 AND $5) - ) - ) - AND ("AssetEntity"."deletedAt" IS NULL) -ORDER BY - "AssetEntity"."fileCreatedAt" DESC - -- AssetRepository.getByIds SELECT "AssetEntity"."id" AS "AssetEntity_id", diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 592839254..dc8d21f00 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { DateTime } from 'luxon'; import path from 'node:path'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { AssetOrder } from 'src/entities/album.entity'; @@ -76,41 +75,6 @@ export class AssetRepository implements IAssetRepository { return this.repository.save(asset); } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.DATE] }) - getByDate(ownerId: string, date: Date): Promise { - // For reference of a correct approach although slower - - // let builder = this.repository - // .createQueryBuilder('asset') - // .leftJoin('asset.exifInfo', 'exifInfo') - // .where('asset.ownerId = :ownerId', { ownerId }) - // .andWhere( - // `coalesce(date_trunc('day', asset."fileCreatedAt", "exifInfo"."timeZone") at TIME ZONE "exifInfo"."timeZone", date_trunc('day', asset."fileCreatedAt")) IN (:date)`, - // { date }, - // ) - // .andWhere('asset.isVisible = true') - // .andWhere('asset.isArchived = false') - // .orderBy('asset.fileCreatedAt', 'DESC'); - - // return builder.getMany(); - - return this.repository.find({ - where: { - ownerId, - isVisible: true, - isArchived: false, - resizePath: Not(IsNull()), - fileCreatedAt: OptionalBetween(date, DateTime.fromJSDate(date).plus({ day: 1 }).toJSDate()), - }, - relations: { - exifInfo: true, - }, - order: { - fileCreatedAt: 'DESC', - }, - }); - } - @GenerateSql({ params: [DummyValue.UUID, { day: 1, month: 1 }] }) getByDayOfYear(ownerIds: string[], { day, month }: MonthDay): Promise { return this.repository diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 22d4f3707..67770cd93 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -5,7 +5,6 @@ export const newAssetRepositoryMock = (): jest.Mocked => { create: jest.fn(), upsertExif: jest.fn(), upsertJobStatus: jest.fn(), - getByDate: jest.fn(), getByDayOfYear: jest.fn(), getByIds: jest.fn().mockResolvedValue([]), getByIdsWithAllRelations: jest.fn().mockResolvedValue([]), From 787eebcf1e09404b1169105bce0f3f6fbc6e300c Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Sat, 23 Mar 2024 14:33:25 -0400 Subject: [PATCH 03/21] refactor(server): new password repo method (#8208) --- server/src/constants.ts | 2 ++ server/src/cores/user.core.ts | 3 +-- server/src/interfaces/crypto.interface.ts | 1 + server/src/repositories/crypto.repository.ts | 4 ++++ server/src/services/api-key.service.spec.ts | 4 ++-- server/src/services/api-key.service.ts | 2 +- server/src/services/auth.service.ts | 3 +-- server/src/services/user.service.ts | 5 ++--- server/test/repositories/crypto.repository.mock.ts | 1 + 9 files changed, 15 insertions(+), 10 deletions(-) diff --git a/server/src/constants.ts b/server/src/constants.ts index f6cef9059..1289701dd 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -3,6 +3,8 @@ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { Version } from 'src/utils/version'; +export const SALT_ROUNDS = 10; + const { version } = JSON.parse(readFileSync('./package.json', 'utf8')); export const serverVersion = Version.fromString(version); diff --git a/server/src/cores/user.core.ts b/server/src/cores/user.core.ts index 4d7da25de..e8596db3e 100644 --- a/server/src/cores/user.core.ts +++ b/server/src/cores/user.core.ts @@ -1,5 +1,6 @@ import { BadRequestException, ForbiddenException } from '@nestjs/common'; import sanitize from 'sanitize-filename'; +import { SALT_ROUNDS } from 'src/constants'; import { UserResponseDto } from 'src/dtos/user.dto'; import { LibraryType } from 'src/entities/library.entity'; import { UserEntity } from 'src/entities/user.entity'; @@ -7,8 +8,6 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -const SALT_ROUNDS = 10; - let instance: UserCore | null; export class UserCore { diff --git a/server/src/interfaces/crypto.interface.ts b/server/src/interfaces/crypto.interface.ts index c33ee9cd7..e7ad2b045 100644 --- a/server/src/interfaces/crypto.interface.ts +++ b/server/src/interfaces/crypto.interface.ts @@ -8,4 +8,5 @@ export interface ICryptoRepository { hashSha1(data: string | Buffer): Buffer; hashBcrypt(data: string | Buffer, saltOrRounds: string | number): Promise; compareBcrypt(data: string | Buffer, encrypted: string): boolean; + newPassword(bytes: number): string; } diff --git a/server/src/repositories/crypto.repository.ts b/server/src/repositories/crypto.repository.ts index 84b74052c..9102715a1 100644 --- a/server/src/repositories/crypto.repository.ts +++ b/server/src/repositories/crypto.repository.ts @@ -41,4 +41,8 @@ export class CryptoRepository implements ICryptoRepository { stream.on('end', () => resolve(hash.digest())); }); } + + newPassword(bytes: number) { + return randomBytes(bytes).toString('base64').replaceAll(/\W/g, ''); + } } diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index 3c8463c8f..47fd0f515 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -27,7 +27,7 @@ describe(APIKeyService.name, () => { name: 'Test Key', userId: authStub.admin.user.id, }); - expect(cryptoMock.randomBytes).toHaveBeenCalled(); + expect(cryptoMock.newPassword).toHaveBeenCalled(); expect(cryptoMock.hashSha256).toHaveBeenCalled(); }); @@ -41,7 +41,7 @@ describe(APIKeyService.name, () => { name: 'API Key', userId: authStub.admin.user.id, }); - expect(cryptoMock.randomBytes).toHaveBeenCalled(); + expect(cryptoMock.newPassword).toHaveBeenCalled(); expect(cryptoMock.hashSha256).toHaveBeenCalled(); }); }); diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 5de908b4d..24a57d365 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -13,7 +13,7 @@ export class APIKeyService { ) {} async create(auth: AuthDto, dto: APIKeyCreateDto): Promise { - const secret = this.crypto.randomBytes(32).toString('base64').replaceAll(/\W/g, ''); + const secret = this.crypto.newPassword(32); const entity = await this.repository.create({ key: this.crypto.hashSha256(secret), name: dto.name || 'API Key', diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 8563d8353..19476667c 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -146,7 +146,6 @@ export class AuthService { async adminSignUp(dto: SignUpDto): Promise { const adminUser = await this.userRepository.getAdmin(); - if (adminUser) { throw new BadRequestException('The server already has an admin'); } @@ -427,7 +426,7 @@ export class AuthService { } private async createLoginResponse(user: UserEntity, authType: AuthType, loginDetails: LoginDetails) { - const key = this.cryptoRepository.randomBytes(32).toString('base64').replaceAll(/\W/g, ''); + const key = this.cryptoRepository.newPassword(32); const token = this.cryptoRepository.hashSha256(key); await this.userTokenRepository.create({ diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index 6649927da..a2bc8c7cf 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundException } from '@nestjs/common'; import { DateTime } from 'luxon'; -import { randomBytes } from 'node:crypto'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { UserCore } from 'src/cores/user.core'; @@ -26,7 +25,7 @@ export class UserService { constructor( @Inject(IAlbumRepository) private albumRepository: IAlbumRepository, - @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, + @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ILibraryRepository) libraryRepository: ILibraryRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @@ -132,7 +131,7 @@ export class UserService { } const providedPassword = await ask(mapUser(admin)); - const password = providedPassword || randomBytes(24).toString('base64').replaceAll(/\W/g, ''); + const password = providedPassword || this.cryptoRepository.newPassword(24); await this.userCore.updateUser(admin, admin.id, { password }); diff --git a/server/test/repositories/crypto.repository.mock.ts b/server/test/repositories/crypto.repository.mock.ts index cbd90ec67..8d13814db 100644 --- a/server/test/repositories/crypto.repository.mock.ts +++ b/server/test/repositories/crypto.repository.mock.ts @@ -9,5 +9,6 @@ export const newCryptoRepositoryMock = (): jest.Mocked => { hashSha256: jest.fn().mockImplementation((input) => `${input} (hashed)`), hashSha1: jest.fn().mockImplementation((input) => Buffer.from(`${input.toString()} (hashed)`)), hashFile: jest.fn().mockImplementation((input) => `${input} (file-hashed)`), + newPassword: jest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')), }; }; From b07a565e34b2e67741032e2e132f3e3ea025b85b Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Sat, 23 Mar 2024 14:37:06 -0400 Subject: [PATCH 04/21] chore(server): change upsert signature for search repo (#8210) * upsert embedding * remove unused imports --- server/src/interfaces/search.interface.ts | 3 +-- server/src/repositories/search.repository.ts | 12 +----------- server/src/services/smart-info.service.spec.ts | 8 +------- server/src/services/smart-info.service.ts | 2 +- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/server/src/interfaces/search.interface.ts b/server/src/interfaces/search.interface.ts index a4be05549..1287202ad 100644 --- a/server/src/interfaces/search.interface.ts +++ b/server/src/interfaces/search.interface.ts @@ -1,7 +1,6 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; -import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { Paginated } from 'src/utils/pagination'; export const ISearchRepository = 'ISearchRepository'; @@ -188,7 +187,7 @@ export interface ISearchRepository { searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated; searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated; searchFaces(search: FaceEmbeddingSearch): Promise; - upsert(smartInfo: Partial, embedding?: Embedding): Promise; + upsert(assetId: string, embedding: number[]): Promise; searchPlaces(placeName: string): Promise; getAssetsByCity(userIds: string[]): Promise; deleteAllSearchEmbeddings(): Promise; diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index a151c0750..85cb45b3d 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -10,7 +10,6 @@ import { SmartSearchEntity } from 'src/entities/smart-search.entity'; import { DatabaseExtension } from 'src/interfaces/database.interface'; import { AssetSearchOptions, - Embedding, FaceEmbeddingSearch, FaceSearchResult, ISearchRepository, @@ -247,16 +246,7 @@ export class SearchRepository implements ISearchRepository { return items; } - async upsert(smartInfo: Partial, embedding?: Embedding): Promise { - await this.repository.upsert(smartInfo, { conflictPaths: ['assetId'] }); - if (!smartInfo.assetId || !embedding) { - return; - } - - await this.upsertEmbedding(smartInfo.assetId, embedding); - } - - private async upsertEmbedding(assetId: string, embedding: number[]): Promise { + async upsert(assetId: string, embedding: number[]): Promise { await this.smartSearchRepository.upsert( { assetId, embedding: () => asVector(embedding, true) }, { conflictPaths: ['assetId'] }, diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index 81d7935c3..968aeac5f 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -104,7 +104,6 @@ describe(SmartInfoService.name, () => { }); it('should save the returned objects', async () => { - searchMock.upsert.mockResolvedValue(); machineMock.encodeImage.mockResolvedValue([0.01, 0.02, 0.03]); await sut.handleEncodeClip({ id: asset.id }); @@ -114,12 +113,7 @@ describe(SmartInfoService.name, () => { { imagePath: 'path/to/resize.ext' }, { enabled: true, modelName: 'ViT-B-32__openai' }, ); - expect(searchMock.upsert).toHaveBeenCalledWith( - { - assetId: 'asset-1', - }, - [0.01, 0.02, 0.03], - ); + expect(searchMock.upsert).toHaveBeenCalledWith('asset-1', [0.01, 0.02, 0.03]); }); }); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index fb19e90a7..183c45b80 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -98,7 +98,7 @@ export class SmartInfoService { await this.databaseRepository.wait(DatabaseLock.CLIPDimSize); } - await this.repository.upsert({ assetId: asset.id }, clipEmbedding); + await this.repository.upsert(asset.id, clipEmbedding); return JobStatus.SUCCESS; } From b449feb3e17bedfa5680aba061895bee72a2d14a Mon Sep 17 00:00:00 2001 From: mmomjian <50788000+mmomjian@users.noreply.github.com> Date: Sat, 23 Mar 2024 15:21:40 -0400 Subject: [PATCH 05/21] docs: Fix documentation for running as a non-root Docker user (#8218) * Update FAQ.mdx * Update FAQ.mdx * Update FAQ.mdx --- docs/docs/FAQ.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 1465cc9fd..884beee4b 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -288,7 +288,11 @@ Immich components are typically deployed using docker. To see logs for deployed ### How can I run Immich as a non-root user? You can change the user in the container by setting the `user` argument in `docker-compose.yml` for each service. -You may need to add an additional volume to `immich-microservices` that mounts internally to `/usr/src/app/.reverse-geocoding-dump`. +You may need to add mount points or docker volumes for the following internal container paths: + +- `immich-machine-learning:/.config` +- `immich-machine-learning:/.cache` +- `redis:/data` The non-root user/group needs read/write access to the volume mounts, including `UPLOAD_LOCATION`. From 3cc800f93a788e390633b422166d814977fa8798 Mon Sep 17 00:00:00 2001 From: aviv926 <51673860+aviv926@users.noreply.github.com> Date: Sat, 23 Mar 2024 21:43:10 +0200 Subject: [PATCH 06/21] feat(docs): New repair and statistics pages (#8030) * New repair and statistics page * PR Feedback * New * chore: cleanup --------- Co-authored-by: Jason Rasmussen --- docs/blog/2022/11-10/release-1.36.mdx | 4 +-- .../docs/administration/img/repair-page-1.png | Bin 0 -> 69820 bytes docs/docs/administration/img/repair-page.png | Bin 0 -> 54837 bytes docs/docs/administration/img/server-stats.png | Bin 0 -> 61867 bytes docs/docs/administration/repair-page.md | 31 ++++++++++++++++++ docs/docs/administration/server-stats.md | 13 ++++++++ 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 docs/docs/administration/img/repair-page-1.png create mode 100644 docs/docs/administration/img/repair-page.png create mode 100644 docs/docs/administration/img/server-stats.png create mode 100644 docs/docs/administration/repair-page.md create mode 100644 docs/docs/administration/server-stats.md diff --git a/docs/blog/2022/11-10/release-1.36.mdx b/docs/blog/2022/11-10/release-1.36.mdx index 9980c1a05..5f5643196 100644 --- a/docs/blog/2022/11-10/release-1.36.mdx +++ b/docs/blog/2022/11-10/release-1.36.mdx @@ -10,8 +10,8 @@ Hello everyone, it is my pleasure to deliver the new release of Immich to you. T Some notable features are: -- [OAuth integration](#livephoto-ios-support-) -- [LivePhoto support on iOS](#oauth-integration-) +- OAuth integration +- LivePhoto support on iOS - User config system diff --git a/docs/docs/administration/img/repair-page-1.png b/docs/docs/administration/img/repair-page-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4c6a1cdddb6ea48cf6c2908ad32d6f7823ac87ce GIT binary patch literal 69820 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 Jc<}h;{{fS&-}wLl literal 0 HcmV?d00001 diff --git a/docs/docs/administration/img/repair-page.png b/docs/docs/administration/img/repair-page.png new file mode 100644 index 0000000000000000000000000000000000000000..b6ae7e40b1e8e6d9e778d9bf7830fdd5eeb23e4d GIT binary patch literal 54837 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 bVk5dyEWhn8fKPrekRYR(ViriRS3K|m{6XVzY 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 z%0A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 + +## Any Other Situation + +:::note RAM Usage +Several users report a situation where the page fails to load. In order to solve this problem you should try to allocate more RAM to Immich, if the problem continues, you should stop using the reverse proxy while loading the page. +::: + +In any other situation, there are 3 different options that can appear: + +- MATCHES - These files are matched by their checksums. + +- OFFLINE PATHS - These files are the result of manually deleting files in the upload library or a failed file move in the past (losing track of a file). + +:::tip +To get rid of Offline paths you can follow this [guide](/docs/guides/remove-offline-files.md) +::: + +- UNTRACKED FILES - These files are not tracked by the application. They can be the result of failed moves, interrupted uploads, or left behind due to a bug. + +In addition, you can download the information from a page, mark everything (in order to check hashing) and correct the problem if a match is found in the hashing. + + diff --git a/docs/docs/administration/server-stats.md b/docs/docs/administration/server-stats.md new file mode 100644 index 000000000..61f4d2d00 --- /dev/null +++ b/docs/docs/administration/server-stats.md @@ -0,0 +1,13 @@ +# Server Stats + +Server statistics to show the total number of videos, photos, and usage per user. + +:::info +If a storage quota has been defined for the user, the usage number will be displayed as a percentage of the total storage quota allocated to him. +::: + +:::info External library +External library is not included in the storage quota. +::: + + From a771c563badd6f74bbf44d728334a5b359bc6725 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Sat, 23 Mar 2024 16:07:39 -0400 Subject: [PATCH 07/21] chore(server): remove pre-installed cli (#8224) --- docs/docs/features/command-line-interface.md | 2 +- server/bin/immich | 3 -- server/package-lock.json | 33 -------------------- server/package.json | 1 - 4 files changed, 1 insertion(+), 38 deletions(-) delete mode 100755 server/bin/immich diff --git a/docs/docs/features/command-line-interface.md b/docs/docs/features/command-line-interface.md index d4c3a1d8e..bb34f8a0f 100644 --- a/docs/docs/features/command-line-interface.md +++ b/docs/docs/features/command-line-interface.md @@ -1,6 +1,6 @@ # The Immich CLI -Immich has a CLI that allows you to perform certain actions from the command line. This CLI replaces the [legacy CLI](https://github.com/immich-app/CLI) that was previously available. The CLI is hosted in the [cli folder of the the main Immich github repository](https://github.com/immich-app/immich/tree/main/cli). +Immich has a command line interface (CLI) that allows you to perform certain actions from the command line. ## Features diff --git a/server/bin/immich b/server/bin/immich deleted file mode 100755 index 6b7dc3aa3..000000000 --- a/server/bin/immich +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -node /usr/src/app/node_modules/.bin/immich "$@" diff --git a/server/package-lock.json b/server/package-lock.json index d36229b14..4e82172cb 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,7 +10,6 @@ "license": "GNU Affero General Public License version 3", "dependencies": { "@babel/runtime": "^7.22.11", - "@immich/cli": "^2.0.7", "@nestjs/bullmq": "^10.0.1", "@nestjs/common": "^10.2.2", "@nestjs/config": "^3.0.0", @@ -1718,20 +1717,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@immich/cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@immich/cli/-/cli-2.1.0.tgz", - "integrity": "sha512-3s/8+Js1dAwibzgaRtZ+bsAL9nOtvoEX/qMlOTgbgLf/lT96M88QScqhb+YrU2l3WBugtts6xW76XQTrWGXcmw==", - "dependencies": { - "lodash-es": "^4.17.21" - }, - "bin": { - "immich": "dist/index.js" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -10152,11 +10137,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -15604,14 +15584,6 @@ "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", "optional": true }, - "@immich/cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@immich/cli/-/cli-2.1.0.tgz", - "integrity": "sha512-3s/8+Js1dAwibzgaRtZ+bsAL9nOtvoEX/qMlOTgbgLf/lT96M88QScqhb+YrU2l3WBugtts6xW76XQTrWGXcmw==", - "requires": { - "lodash-es": "^4.17.21" - } - }, "@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -21877,11 +21849,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", diff --git a/server/package.json b/server/package.json index ead525a6e..45d719af9 100644 --- a/server/package.json +++ b/server/package.json @@ -34,7 +34,6 @@ }, "dependencies": { "@babel/runtime": "^7.22.11", - "@immich/cli": "^2.0.7", "@nestjs/bullmq": "^10.0.1", "@nestjs/common": "^10.2.2", "@nestjs/config": "^3.0.0", From c85563da50e19c249d1e98d8b804c1b2d7ed6ad7 Mon Sep 17 00:00:00 2001 From: Timothy Pillow Date: Sat, 23 Mar 2024 21:24:53 +0100 Subject: [PATCH 08/21] Update command-line-interface.md (#8213) * Update command-line-interface.md Update documentation for CLI commands. * chore: update docs * chore: login-key => login --------- Co-authored-by: Jason Rasmussen --- docs/docs/features/command-line-interface.md | 42 +++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/docs/features/command-line-interface.md b/docs/docs/features/command-line-interface.md index bb34f8a0f..094de609f 100644 --- a/docs/docs/features/command-line-interface.md +++ b/docs/docs/features/command-line-interface.md @@ -54,16 +54,19 @@ Usage: immich [options] [command] Command line interface for Immich Options: - -V, --version output the version number - -d, --config Configuration directory (env: IMMICH_CONFIG_DIR) - -h, --help display help for command + -V, --version output the version number + -d, --config-directory Configuration directory where auth.yml will be stored (default: "~/.config/immich/", env: + IMMICH_CONFIG_DIR) + -u, --url [url] Immich server URL (env: IMMICH_INSTANCE_URL) + -k, --key [key] Immich API key (env: IMMICH_API_KEY) + -h, --help display help for command Commands: - upload [options] [paths...] Upload assets - server-info Display server information - login [url] [key] Login using an API key - logout Remove stored credentials - help [command] display help for command + login|login-key Login using an API key + logout Remove stored credentials + server-info Display server information + upload [options] [paths...] Upload assets + help [command] display help for command ``` ## Commands @@ -71,23 +74,24 @@ Commands: The upload command supports the following options: ``` -Usage: immich upload [options] [paths...] +Usage: immich upload [paths...] [options] Upload assets Arguments: - paths One or more paths to assets to be uploaded + paths One or more paths to assets to be uploaded Options: - -r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE) - -i, --ignore [paths...] Paths to ignore (env: IMMICH_IGNORE_PATHS) - -h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH) - -H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN) - -a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM) - -A, --album-name Add all assets to specified album (env: IMMICH_ALBUM_NAME) - -n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN) - --delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS) - --help display help for command + -r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE) + -i, --ignore [paths...] Paths to ignore (default: [], env: IMMICH_IGNORE_PATHS) + -h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH) + -H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN) + -a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM) + -A, --album-name Add all assets to specified album (env: IMMICH_ALBUM_NAME) + -n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN) + -c, --concurrency Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY) + --delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS) + --help display help for command ``` Note that the above options can read from environment variables as well. From 727b3b9f53ecf7cab9743a9f161e538dfc40b222 Mon Sep 17 00:00:00 2001 From: Jamie <100302760+digitaljamie@users.noreply.github.com> Date: Sun, 24 Mar 2024 01:21:46 +0000 Subject: [PATCH 09/21] chore(docs): update storage-template.md (#8154) Update _storage-template.md --- docs/docs/partials/_storage-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/partials/_storage-template.md b/docs/docs/partials/_storage-template.md index cf9313042..b52fd4649 100644 --- a/docs/docs/partials/_storage-template.md +++ b/docs/docs/partials/_storage-template.md @@ -1,7 +1,7 @@ Immich allows the admin user to set the uploaded filename pattern. Both at the directory and filename level. :::note new version -On new machines running version 1.92.0 storage template engine is off by default, for [more info](https://github.com/immich-app/immich/releases#:~:text=the%20partner%E2%80%99s%20assets.-,Hardening%20storage%20template,-We%20have%20further). +On new machines running version 1.92.0 storage template engine is off by default, for [more info](https://github.com/immich-app/immich/releases/tag/v1.92.0#:~:text=the%20partner%E2%80%99s%20assets.-,Hardening%20storage%20template,-We%20have%20further). ::: :::tip From d36d32d07bc781953219ffed8dc5c656d3af355f Mon Sep 17 00:00:00 2001 From: aviv926 <51673860+aviv926@users.noreply.github.com> Date: Sun, 24 Mar 2024 04:04:32 +0200 Subject: [PATCH 10/21] feat(docs): Changes to the Administration section. (#8078) * Changes to the administration section * Add -> * chore: better explanation about jobs * chore: improve system config wording * chore: remove duplicate entry --------- Co-authored-by: Jason Rasmussen --- docs/docs/administration/jobs.md | 8 +- docs/docs/administration/password-login.md | 32 ---- docs/docs/administration/system-settings.md | 173 ++++++++++++++++++++ docs/static/_redirects | 1 + 4 files changed, 180 insertions(+), 34 deletions(-) delete mode 100644 docs/docs/administration/password-login.md create mode 100644 docs/docs/administration/system-settings.md diff --git a/docs/docs/administration/jobs.md b/docs/docs/administration/jobs.md index e02bfcaed..9c8536cfa 100644 --- a/docs/docs/administration/jobs.md +++ b/docs/docs/administration/jobs.md @@ -1,9 +1,13 @@ # Jobs -Several Immich functionalities are implemented as jobs, which run in the background. To view the status of a job navigate to the Administration Screen, and then the `Jobs` page. +The `immich-server` responds to API requests for data and files for the web and mobile app. To do this quickly and reliably, it offloads most other work to `immich-microservices` in the form of _jobs_. Simply put, a job is a request to process data in the background. Jobs are picked up automatically by microservices containers. -![Admin jobs](./img/admin-jobs.png) +When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page. + +Additionally, some jobs run on a schedule, which is every night at midnight. This schedule, with the exception of [External Libraries](/docs/features/libraries) scanning, cannot be changed. :::info Storage Migration job can be run after changing the [Storage Template](/docs/administration/storage-template.mdx), in order to apply the change to the existing library. ::: + + diff --git a/docs/docs/administration/password-login.md b/docs/docs/administration/password-login.md deleted file mode 100644 index ff4852eee..000000000 --- a/docs/docs/administration/password-login.md +++ /dev/null @@ -1,32 +0,0 @@ -# Password Login - -An overview of password login and related settings for Immich. - -## Enable/Disable - -Immich supports password login, which is enabled by default. The preferred way to disable it is via the [Administration Page](#administration-page), although it can also be changed via a [Server Command](#server-command) as well. - -### Administration Page - -To toggle the password login setting via the web, navigate to the "Administration", expand "Password Authentication", toggle the "Enabled" switch, and press "Save". - -![Password Login Settings](./img/password-login-settings.png) - -### Server Command - -There are two [Server Commands](/docs/administration/server-commands.md) for password login: - -1. `enable-password-login` -2. `disable-password-login` - -See [Server Commands](/docs/administration/server-commands.md) for more details about how to run them. - -## Password Reset - -### Admin - -To reset the administrator password, use the `reset-admin-password` [Server Command](/docs/administration/server-commands.md). - -### User - -Immich does not currently support self-service password reset. However, the administration can reset passwords for other users. See [User Management: Password Reset](/docs/administration/user-management.mdx#password-reset) for more information about how to do this. diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md new file mode 100644 index 000000000..21eeaaee8 --- /dev/null +++ b/docs/docs/administration/system-settings.md @@ -0,0 +1,173 @@ +# System Settings + +On the system settings page, the administrator can manage global settings for the Immich instance. + +:::note +Viewing and modifying the system settings is restricted to the Administrator. +::: + +:::tip +You can always return to the default settings by clicking the `Reset to default` button. +::: + +## Job Settings + +Using these settings, you can determine the amount of work that will run concurrently for each task in microservices. Some tasks can be set to higher values on computers with powerful hardware and storage with good I/O capabilities. + +With higher concurrency, the host will work on more assets in parallel, +this advice improves throughput, not latency, for example, it will make Smart Search jobs process more quickly, but it won't make searching faster. + +It is important to remember that jobs like Smart Search, Face Detection, Facial Recognition, and Transcode Videos require a **lot** of processing power and therefore do not exaggerate the amount of jobs because you're probably thoroughly overloading the server. + +:::info Facial Recognition Concurrency +The Facial Recognition Concurrency value cannot be changed because +[DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok) is traditionally sequential, but there are parallel implementations of it out there. Our implementation isn't parallel. +::: + +## External Library + +### Library watching (EXPERIMENTAL) + +External libraries can automatically import changed files without a full rescan. It will import the file whenever the operating system reports a file change. If your photos are mounted over the network, this does not work. + +### Periodic Scanning + +You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library. +You can set the scanning interval using the preset or cron format. For more information please refer to e.g. [Crontab Guru](https://crontab.guru/). + +## Logging + +By default logs are set to record at the log level, the network administrator can choose a deeper or lower level of logs according to his decision or according to the needs required by the Immich support team. + +Here you can [learn about the different error levels](https://sematext.com/blog/logging-levels/). + +## Machine Learning Settings + +Through this setting, you can manage all the settings related to machine learning in Immich, from the setting of remote machine learning to the model and its parameters +You can choose to disable a certain type of machine learning, for example smart search or facial recognition. + +### Smart Search + +The smart search settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the +Smart Search job on all images to fully apply the change. + +:::info Internet connection +Changing models requires a connection to the Internet to download the model. +After downloading, there is no need for Immich to connect to the network +Unless version checking has been enabled in the settings. +::: + +### Facial Recognition + +Under these settings, you can change the facial recognition settings +Editable settings: + +- **Facial Recognition Model -** Models are listed in descending order of size. Larger models are slower and use more memory, but produce better results. Note that you must re-run the Face Detection job for all images upon changing a model. +- **Min Detection Score -** Minimum confidence score for a face to be detected from 0-1. Lower values will detect more faces but may result in false positives. +- **Max Recognition Distance -** Maximum distance between two faces to be considered the same person, ranging from 0-2. Lowering this can prevent labeling two people as the same person, while raising it can prevent labeling the same person as two different people. Note that it is easier to merge two people than to split one person in two, so err on the side of a lower threshold when possible. +- **Min Recognized Faces -** The minimum number of recognized faces for a person to be created (AKA: Core face). Increasing this makes Facial Recognition more precise at the cost of increasing the chance that a face is not assigned to a person. + +:::info +When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces. +You will have to restart **only** the job FACIAL RECOGNITION - ALL. + +If you replace the Facial Recognition Model, you will have to run the job FACE DETECTION - ALL. +::: + +:::tip identical twins +If you have twins, you might want to lower the Max Recognition Distance value, decreasing this a **bit** can make it distinguish between them. +::: + +## Map & GPS Settings + +### Map Settings + +In these settings, you can change the appearance of the map in night and day modes according to your personal preference and according to the supported options. +The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for example. + +### Reverse Geocoding Settings + +Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. + +## OAuth Authentication + +Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth). + +## Password Authentication + +The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts. + +:::tip +You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login. +::: + +## Server Settings + +### External Domain + +When set, will override the domain name used when viewing and copying a shared link. + +### Welcome Message + +The administrator can set a custom message on the login screen (the message will be displayed to all users). + +## Storage Template + +Immich supports a custom [Storage Template](/docs/administration/storage-template). Learn more about this feature and its configuration [here](/docs/administration/storage-template). + +## Theme Settings + +You can write custom CSS that will get loaded in the web application for all users. This enables administrators to change fonts, colors, and other styles. + +For example: + +```css title='Custom CSS' +p { + color: green; +} +``` + +## Thumbnail Settings + +By default Immich creates 3 thumbnails for each asset, +Blurred (thumbhash) , Small (webp) , and Large (jpeg), using these settings you can change the quality for the thumbnail files that are created. + +**Small thumbnail resolution** +Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness. + +**Large thumbnail resolution** +Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness. + +**Quality** +Thumbnail quality from 1-100. Higher is better for quality but produces larger files. + +**Prefer wide gamut** +Use display p3 for thumbnails. This better preserves the vibrance of images with wide color spaces, but images may appear differently on old devices with an old browser version. Srgb images are kept as srgb to avoid color shifts. + +:::tip +The default resolution for Large thumbnails can be lowered from 1440p (default) to 1080p or 720p to save storage space. +::: + +## Trash Settings + +In the system administrator's option to set a trash for deleted files, these files will remain in the trash until the deletion date 30 days (default) or as defined by the system administrator. + +The trash can be disabled, however this is not recommended as future files that are deleted will be permanently deleted. + +:::tip Keyboard shortcut for permanently deletion +You can select assets and press Ctrl + Del from the timeline for quick permanent deletion without the trash option. +::: + +## User Settings + +### Delete delay + +The system administrator can choose to delete users through the administration panel, the system administrator can delete users immediately or alternatively delay the deletion for users (7 days by default) this action permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution. + +## Version Check + +When this option is enabled the `immich-server` will periodically make requests to GitHub to check for new releases. + +## Video Transcoding Settings + +The system administrator can define parameters according to which video files will be converted to different formats (depending on the settings). The settings can be changed in depth, to learn more about the terminology used here, refer to FFmpeg documentation for [H.264](https://trac.ffmpeg.org/wiki/Encode/H.264) codec, [HEVC](https://trac.ffmpeg.org/wiki/Encode/H.265) codec and [VP9](https://trac.ffmpeg.org/wiki/Encode/VP9) codec. diff --git a/docs/static/_redirects b/docs/static/_redirects index b12fee69a..5f2d5c5a1 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -24,3 +24,4 @@ /docs/features/user-management /docs/administration/user-management 301 /docs/developer/contributing /docs/developer/pr-checklist 301 /docs/guides/machine-learning /docs/guides/remote-machine-learning 301 +/docs/administration/password-login /docs/administration/system-settings 301 From 96a5710932bebf9c7d698b75093cbd37b1d93b2c Mon Sep 17 00:00:00 2001 From: Kokul Shanmugharajah Date: Sun, 24 Mar 2024 05:45:42 -0700 Subject: [PATCH 11/21] (docs) Update XMP sidecar docs to include the fact that Immich will look for photo.ext.xmp and photo.xmp (#8235) Update XMP sidecar docs --- docs/docs/features/xmp-sidecars.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/features/xmp-sidecars.md b/docs/docs/features/xmp-sidecars.md index d184ce227..371547aa7 100644 --- a/docs/docs/features/xmp-sidecars.md +++ b/docs/docs/features/xmp-sidecars.md @@ -6,7 +6,7 @@ Immich can ingest XMP sidecars on file upload (via the CLI) as well as detect ne XMP sidecars are external XML files that contain metadata related to media files. Many applications read and write these files either exclusively or in addition to the metadata written to image files. They can be a powerful tool for editing and storing metadata of a media file without modifying the media file itself. When Immich receives or detects an XMP sidecar for a media file, it will attempt to extract the metadata from both the sidecar as well as the media file. It will prioritize the metadata for fields in the sidecar but will fall back and use the metadata in the media file if necessary. -When importing files via the CLI bulk uploader, Immich will automatically detect XMP sidecar files as files that exist next to the original media file and have the exact same name with an additional `.xmp` file extension (i.e., `PXL_20230401_203352928.MP.jpg` and `PXL_20230401_203352928.MP.jpg.xmp`). +When importing files via the CLI bulk uploader or parsing photo metadata for external libraries, Immich will automatically detect XMP sidecar files as files that exist next to the original media file. Immich will look files that have the same name as the photo, but with the `.xmp` file extension. The same name can either include the photo's file extension or without the photo's file extension. For example, for a photo named `PXL_20230401_203352928.MP.jpg`, Immich will look for an XMP file named either `PXL_20230401_203352928.MP.jpg.xmp` or `PXL_20230401_203352928.MP.xmp`. If both `PXL_20230401_203352928.MP.jpg.xmp` and `PXL_20230401_203352928.MP.xmp` are present, Immich will prefer `PXL_20230401_203352928.MP.jpg.xmp`. There are 2 administrator jobs associated with sidecar files: `SYNC` and `DISCOVER`. The sync job will re-scan all media with existing sidecar files and queue them for a metadata refresh. This is a great use case when third-party applications are used to modify the metadata of media. The discover job will attempt to scan the filesystem for new sidecar files for all media that does not currently have a sidecar file associated with it. From 5dc59b591d6f9428665c8654064ada09e1fb1daf Mon Sep 17 00:00:00 2001 From: martin <74269598+martabal@users.noreply.github.com> Date: Sun, 24 Mar 2024 19:07:20 +0100 Subject: [PATCH 12/21] refactor(web): albums list (2) (#8214) * refactor: albums list * fix: responsive design * keep albums in sharing --- .../album-page/albums-controls.svelte | 14 +++-- .../components/album-page/albums-list.svelte | 52 ++++++++++++++++--- .../components/album-page/albums-table.svelte | 27 +++++----- .../lib/components/elements/group-tab.svelte | 20 +++++++ web/src/lib/stores/preferences.store.ts | 8 +++ web/src/routes/(user)/albums/+page.svelte | 2 +- web/src/routes/(user)/albums/+page.ts | 2 + 7 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 web/src/lib/components/elements/group-tab.svelte diff --git a/web/src/lib/components/album-page/albums-controls.svelte b/web/src/lib/components/album-page/albums-controls.svelte index a58dbc0e3..b391b061d 100644 --- a/web/src/lib/components/album-page/albums-controls.svelte +++ b/web/src/lib/components/album-page/albums-controls.svelte @@ -2,7 +2,7 @@ import LinkButton from '$lib/components/elements/buttons/link-button.svelte'; import Dropdown from '$lib/components/elements/dropdown.svelte'; import Icon from '$lib/components/elements/icon.svelte'; - import { AlbumViewMode, albumViewSettings } from '$lib/stores/preferences.store'; + import { AlbumFilter, AlbumViewMode, albumViewSettings } from '$lib/stores/preferences.store'; import { mdiArrowDownThin, mdiArrowUpThin, @@ -12,6 +12,7 @@ } from '@mdi/js'; import { sortByOptions, type Sort, handleCreateAlbum } from '$lib/components/album-page/albums-list.svelte'; import SearchBar from '$lib/components/elements/search-bar.svelte'; + import GroupTab from '$lib/components/elements/group-tab.svelte'; export let searchAlbum: string; @@ -25,13 +26,20 @@ }; -