From 0c13c63bb62ab6e16730a1bab337a57dc12acd5a Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 20 Mar 2024 16:46:59 -0500 Subject: [PATCH 01/33] refactor: infra/domain module (#8130) --- server/src/apps/api.module.ts | 6 +- .../infra.module.ts => apps/app.module.ts} | 68 ++++++++++++++++--- server/src/apps/immich-admin.module.ts | 5 +- server/src/apps/microservices.module.ts | 5 +- server/src/domain/domain.module.ts | 62 ----------------- server/test/utils.ts | 6 +- 6 files changed, 69 insertions(+), 83 deletions(-) rename server/src/{infra/infra.module.ts => apps/app.module.ts} (73%) delete mode 100644 server/src/domain/domain.module.ts diff --git a/server/src/apps/api.module.ts b/server/src/apps/api.module.ts index 04c95f199..717b348de 100644 --- a/server/src/apps/api.module.ts +++ b/server/src/apps/api.module.ts @@ -3,6 +3,7 @@ import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { ScheduleModule } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ApiService } from 'src/apps/api.service'; +import { AppModule } from 'src/apps/app.module'; import { ActivityController } from 'src/controllers/activity.controller'; import { AlbumController } from 'src/controllers/album.controller'; import { APIKeyController } from 'src/controllers/api-key.controller'; @@ -24,13 +25,11 @@ import { SystemConfigController } from 'src/controllers/system-config.controller import { TagController } from 'src/controllers/tag.controller'; import { TrashController } from 'src/controllers/trash.controller'; import { UserController } from 'src/controllers/user.controller'; -import { DomainModule } from 'src/domain/domain.module'; import { AssetEntity } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { AssetRepositoryV1, IAssetRepositoryV1 } from 'src/immich/api-v1/asset/asset-repository'; import { AssetController as AssetControllerV1 } from 'src/immich/api-v1/asset/asset.controller'; import { AssetService as AssetServiceV1 } from 'src/immich/api-v1/asset/asset.service'; -import { InfraModule } from 'src/infra/infra.module'; import { AuthGuard } from 'src/middleware/auth.guard'; import { ErrorInterceptor } from 'src/middleware/error.interceptor'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @@ -38,8 +37,7 @@ import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @Module({ imports: [ // - InfraModule, - DomainModule, + AppModule, ScheduleModule.forRoot(), TypeOrmModule.forFeature([AssetEntity, ExifEntity]), ], diff --git a/server/src/infra/infra.module.ts b/server/src/apps/app.module.ts similarity index 73% rename from server/src/infra/infra.module.ts rename to server/src/apps/app.module.ts index b0387ba96..f134d3363 100644 --- a/server/src/infra/infra.module.ts +++ b/server/src/apps/app.module.ts @@ -6,9 +6,34 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; import { OpenTelemetryModule } from 'nestjs-otel'; import { bullConfig, bullQueues, immichAppConfig } from 'src/config'; +import { ActivityService } from 'src/domain/activity/activity.service'; +import { AlbumService } from 'src/domain/album/album.service'; +import { APIKeyService } from 'src/domain/api-key/api-key.service'; +import { AssetService } from 'src/domain/asset/asset.service'; +import { AuditService } from 'src/domain/audit/audit.service'; +import { AuthService } from 'src/domain/auth/auth.service'; +import { DatabaseService } from 'src/domain/database/database.service'; +import { DownloadService } from 'src/domain/download/download.service'; +import { JobService } from 'src/domain/job/job.service'; +import { LibraryService } from 'src/domain/library/library.service'; +import { MediaService } from 'src/domain/media/media.service'; +import { MetadataService } from 'src/domain/metadata/metadata.service'; +import { PartnerService } from 'src/domain/partner/partner.service'; +import { PersonService } from 'src/domain/person/person.service'; +import { SearchService } from 'src/domain/search/search.service'; +import { ServerInfoService } from 'src/domain/server-info/server-info.service'; +import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; +import { SmartInfoService } from 'src/domain/smart-info/smart-info.service'; +import { StorageTemplateService } from 'src/domain/storage-template/storage-template.service'; +import { StorageService } from 'src/domain/storage/storage.service'; +import { SystemConfigService } from 'src/domain/system-config/system-config.service'; +import { TagService } from 'src/domain/tag/tag.service'; +import { TrashService } from 'src/domain/trash/trash.service'; +import { UserService } from 'src/domain/user/user.service'; import { databaseEntities } from 'src/entities'; import { databaseConfig } from 'src/infra/database.config'; import { otelConfig } from 'src/infra/instrumentation'; +import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IActivityRepository } from 'src/interfaces/activity.repository'; import { IAlbumRepository } from 'src/interfaces/album.repository'; @@ -64,7 +89,35 @@ import { TagRepository } from 'src/repositories/tag.repository'; import { UserTokenRepository } from 'src/repositories/user-token.repository'; import { UserRepository } from 'src/repositories/user.repository'; -const providers: Provider[] = [ +const services: Provider[] = [ + APIKeyService, + ActivityService, + AlbumService, + AssetService, + AuditService, + AuthService, + DatabaseService, + DownloadService, + ImmichLogger, + JobService, + LibraryService, + MediaService, + MetadataService, + PartnerService, + PersonService, + SearchService, + ServerInfoService, + SharedLinkService, + SmartInfoService, + StorageService, + StorageTemplateService, + SystemConfigService, + TagService, + TrashService, + UserService, +]; + +const repositories: Provider[] = [ { provide: IActivityRepository, useClass: ActivityRepository }, { provide: IAccessRepository, useClass: AccessRepository }, { provide: IAlbumRepository, useClass: AlbumRepository }, @@ -92,7 +145,6 @@ const providers: Provider[] = [ { provide: IMediaRepository, useClass: MediaRepository }, { provide: IUserRepository, useClass: UserRepository }, { provide: IUserTokenRepository, useClass: UserTokenRepository }, - SchedulerRegistry, ]; @Global() @@ -107,10 +159,10 @@ const providers: Provider[] = [ BullModule.registerQueue(...bullQueues), OpenTelemetryModule.forRoot(otelConfig), ], - providers: [...providers], - exports: [...providers, BullModule], + providers: [...services, ...repositories, SchedulerRegistry], + exports: [...services, ...repositories, BullModule, SchedulerRegistry], }) -export class InfraModule {} +export class AppModule {} @Global() @Module({ @@ -121,7 +173,7 @@ export class InfraModule {} TypeOrmModule.forFeature(databaseEntities), ScheduleModule, ], - providers: [...providers], - exports: [...providers], + providers: [...services, ...repositories, SchedulerRegistry], + exports: [...services, ...repositories, SchedulerRegistry], }) -export class InfraTestModule {} +export class AppTestModule {} diff --git a/server/src/apps/immich-admin.module.ts b/server/src/apps/immich-admin.module.ts index d0e5cab4c..eff2e9cc0 100644 --- a/server/src/apps/immich-admin.module.ts +++ b/server/src/apps/immich-admin.module.ts @@ -1,13 +1,12 @@ import { Module } from '@nestjs/common'; +import { AppModule } from 'src/apps/app.module'; import { ListUsersCommand } from 'src/commands/list-users.command'; import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login'; import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login'; import { PromptPasswordQuestions, ResetAdminPasswordCommand } from 'src/commands/reset-admin-password.command'; -import { DomainModule } from 'src/domain/domain.module'; -import { InfraModule } from 'src/infra/infra.module'; @Module({ - imports: [InfraModule, DomainModule], + imports: [AppModule], providers: [ ResetAdminPasswordCommand, PromptPasswordQuestions, diff --git a/server/src/apps/microservices.module.ts b/server/src/apps/microservices.module.ts index 3ddfb28f3..d9a28a91c 100644 --- a/server/src/apps/microservices.module.ts +++ b/server/src/apps/microservices.module.ts @@ -1,10 +1,9 @@ import { Module, OnModuleInit } from '@nestjs/common'; +import { AppModule } from 'src/apps/app.module'; import { MicroservicesService } from 'src/apps/microservices.service'; -import { DomainModule } from 'src/domain/domain.module'; -import { InfraModule } from 'src/infra/infra.module'; @Module({ - imports: [InfraModule, DomainModule], + imports: [AppModule], providers: [MicroservicesService], }) export class MicroservicesModule implements OnModuleInit { diff --git a/server/src/domain/domain.module.ts b/server/src/domain/domain.module.ts deleted file mode 100644 index 04d7c51f4..000000000 --- a/server/src/domain/domain.module.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Global, Module, Provider } from '@nestjs/common'; -import { ActivityService } from 'src/domain/activity/activity.service'; -import { AlbumService } from 'src/domain/album/album.service'; -import { APIKeyService } from 'src/domain/api-key/api-key.service'; -import { AssetService } from 'src/domain/asset/asset.service'; -import { AuditService } from 'src/domain/audit/audit.service'; -import { AuthService } from 'src/domain/auth/auth.service'; -import { DatabaseService } from 'src/domain/database/database.service'; -import { DownloadService } from 'src/domain/download/download.service'; -import { JobService } from 'src/domain/job/job.service'; -import { LibraryService } from 'src/domain/library/library.service'; -import { MediaService } from 'src/domain/media/media.service'; -import { MetadataService } from 'src/domain/metadata/metadata.service'; -import { PartnerService } from 'src/domain/partner/partner.service'; -import { PersonService } from 'src/domain/person/person.service'; -import { SearchService } from 'src/domain/search/search.service'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; -import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; -import { SmartInfoService } from 'src/domain/smart-info/smart-info.service'; -import { StorageTemplateService } from 'src/domain/storage-template/storage-template.service'; -import { StorageService } from 'src/domain/storage/storage.service'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; -import { TagService } from 'src/domain/tag/tag.service'; -import { TrashService } from 'src/domain/trash/trash.service'; -import { UserService } from 'src/domain/user/user.service'; -import { ImmichLogger } from 'src/infra/logger'; - -const providers: Provider[] = [ - APIKeyService, - ActivityService, - AlbumService, - AssetService, - AuditService, - AuthService, - DatabaseService, - DownloadService, - ImmichLogger, - JobService, - LibraryService, - MediaService, - MetadataService, - PartnerService, - PersonService, - SearchService, - ServerInfoService, - SharedLinkService, - SmartInfoService, - StorageService, - StorageTemplateService, - SystemConfigService, - TagService, - TrashService, - UserService, -]; - -@Global() -@Module({ - imports: [], - providers: [...providers], - exports: [...providers], -}) -export class DomainModule {} diff --git a/server/test/utils.ts b/server/test/utils.ts index 777a6a00c..f44f6fe17 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -8,10 +8,10 @@ import { EventEmitter } from 'node:stream'; import { Server } from 'node:tls'; import { ApiModule } from 'src/apps/api.module'; import { ApiService } from 'src/apps/api.service'; +import { AppModule, AppTestModule } from 'src/apps/app.module'; import { MicroservicesService } from 'src/apps/microservices.service'; import { QueueName } from 'src/domain/job/job.constants'; import { dataSource } from 'src/infra/database.config'; -import { InfraModule, InfraTestModule } from 'src/infra/infra.module'; import { IJobRepository, JobItem, JobItemHandler } from 'src/interfaces/job.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { StorageEventType } from 'src/interfaces/storage.repository'; @@ -109,8 +109,8 @@ export const testApp = { imports: [ApiModule], providers: [ApiService, MicroservicesService], }) - .overrideModule(InfraModule) - .useModule(InfraTestModule) + .overrideModule(AppModule) + .useModule(AppTestModule) .overrideProvider(IJobRepository) .useClass(JobMock) .overrideProvider(IMediaRepository) From 6d9e7694b1361527660fa4f30d506964ed9daeb9 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:53:07 +0100 Subject: [PATCH 02/33] chore(server): move dtos (#8131) move dtos --- server/e2e/client/asset-api.ts | 2 +- server/e2e/client/auth-api.ts | 4 +- server/e2e/client/library-api.ts | 2 +- .../jobs/specs/library-watcher.e2e-spec.ts | 4 +- server/e2e/jobs/specs/library.e2e-spec.ts | 2 +- .../commands/reset-admin-password.command.ts | 2 +- server/src/controllers/activity.controller.ts | 6 +- server/src/controllers/album.controller.ts | 19 +- server/src/controllers/api-key.controller.ts | 9 +- server/src/controllers/asset.controller.ts | 23 ++- server/src/controllers/audit.controller.ts | 6 +- server/src/controllers/auth.controller.ts | 6 +- server/src/controllers/download.controller.ts | 6 +- server/src/controllers/face.controller.ts | 4 +- server/src/controllers/job.controller.ts | 2 +- server/src/controllers/library.controller.ts | 4 +- server/src/controllers/oauth.controller.ts | 6 +- server/src/controllers/partner.controller.ts | 4 +- server/src/controllers/person.controller.ts | 10 +- server/src/controllers/search.controller.ts | 16 +- .../src/controllers/server-info.controller.ts | 4 +- .../src/controllers/shared-link.controller.ts | 14 +- .../controllers/system-config.controller.ts | 4 +- server/src/controllers/tag.controller.ts | 11 +- server/src/controllers/trash.controller.ts | 4 +- server/src/controllers/user.controller.ts | 10 +- server/src/cores/access.core.ts | 2 +- server/src/cores/system-config.core.ts | 2 +- server/src/cores/user.core.ts | 2 +- .../src/domain/activity/activity.service.ts | 4 +- server/src/domain/activity/activity.spec.ts | 2 +- server/src/domain/album/album.service.spec.ts | 2 +- server/src/domain/album/album.service.ts | 16 +- .../domain/album/dto/album-add-users.dto.ts | 8 - .../src/domain/album/dto/album-create.dto.ts | 19 -- .../src/domain/album/dto/album-update.dto.ts | 25 --- server/src/domain/album/dto/album.dto.ts | 6 - server/src/domain/album/dto/get-albums.dto.ts | 19 -- server/src/domain/api-key/api-key.service.ts | 4 +- server/src/domain/asset/asset.service.spec.ts | 7 +- server/src/domain/asset/asset.service.ts | 39 ++-- server/src/domain/asset/dto/asset-ids.dto.ts | 20 --- .../domain/asset/dto/asset-statistics.dto.ts | 34 ---- server/src/domain/asset/dto/asset.dto.ts | 75 -------- server/src/domain/asset/dto/map-marker.dto.ts | 18 -- .../src/domain/asset/dto/memory-lane.dto.ts | 19 -- .../response-dto/map-marker-response.dto.ts | 21 --- .../response-dto/smart-info-response.dto.ts | 13 -- .../response-dto/time-bucket-response.dto.ts | 9 - server/src/domain/audit/audit.service.ts | 8 +- server/src/domain/auth/auth.service.spec.ts | 2 +- server/src/domain/auth/auth.service.ts | 4 +- .../domain/download/download.service.spec.ts | 2 +- .../src/domain/download/download.service.ts | 6 +- server/src/domain/job/job.service.ts | 4 +- .../domain/library/library.service.spec.ts | 2 +- server/src/domain/library/library.service.ts | 2 +- server/src/domain/media/media.service.ts | 2 +- server/src/domain/media/media.util.ts | 2 +- .../domain/partner/partner.service.spec.ts | 2 +- server/src/domain/partner/partner.service.ts | 6 +- .../src/domain/person/person.service.spec.ts | 4 +- server/src/domain/person/person.service.ts | 8 +- .../search/dto/search-suggestion.dto.ts | 34 ---- .../search-explore.response.dto.ts | 11 -- .../response-dto/search-response.dto.ts | 38 ---- .../src/domain/search/search.service.spec.ts | 4 +- server/src/domain/search/search.service.ts | 13 +- .../domain/server-info/server-info.service.ts | 2 +- .../src/domain/shared-link/shared-link.dto.ts | 75 -------- .../shared-link/shared-link.service.spec.ts | 2 +- .../domain/shared-link/shared-link.service.ts | 12 +- ...stem-config-template-storage-option.dto.ts | 10 -- .../system-config/system-config.service.ts | 4 +- server/src/domain/tag/tag.dto.ts | 21 --- server/src/domain/tag/tag.service.spec.ts | 2 +- server/src/domain/tag/tag.service.ts | 11 +- server/src/domain/trash/trash.service.ts | 4 +- .../user/dto/create-profile-image.dto.ts | 7 - server/src/domain/user/dto/create-user.dto.ts | 61 ------- server/src/domain/user/dto/delete-user.dto.ts | 6 - .../domain/user/dto/update-user.dto.spec.ts | 17 -- server/src/domain/user/dto/update-user.dto.ts | 52 ------ .../create-profile-image-response.dto.ts | 11 -- .../user/response-dto/user-response.dto.ts | 65 ------- server/src/domain/user/user.service.spec.ts | 3 +- server/src/domain/user/user.service.ts | 12 +- .../{domain/activity => dtos}/activity.dto.ts | 2 +- .../album => dtos}/album-response.dto.spec.ts | 2 +- .../album.dto.ts} | 97 ++++++++-- .../{domain/api-key => dtos}/api-key.dto.ts | 0 .../asset-ids.response.dto.ts} | 0 .../asset-response.dto.ts | 24 ++- server/src/dtos/asset.dto.ts | 132 ++++++++++++++ .../src/{domain/audit => dtos}/audit.dto.ts | 0 server/src/{domain/auth => dtos}/auth.dto.ts | 0 .../{domain/download => dtos}/download.dto.ts | 0 .../exif-response.dto.ts => dtos/exif.dto.ts} | 0 server/src/{domain/job => dtos}/job.dto.ts | 0 .../{domain/library => dtos}/library.dto.ts | 0 .../dto => dtos}/model-config.dto.ts | 0 .../{domain/partner => dtos}/partner.dto.ts | 2 +- .../src/{domain/person => dtos}/person.dto.ts | 2 +- .../{domain/search/dto => dtos}/search.dto.ts | 129 +++++++++++++ .../server-info => dtos}/server-info.dto.ts | 2 +- .../shared-link.dto.ts} | 76 +++++++- .../asset-stack.dto.ts => dtos/stack.dto.ts} | 0 .../dto => dtos}/system-config-ffmpeg.dto.ts | 0 .../dto => dtos}/system-config-job.dto.ts | 0 .../dto => dtos}/system-config-library.dto.ts | 0 .../dto => dtos}/system-config-logging.dto.ts | 0 .../system-config-machine-learning.dto.ts | 2 +- .../dto => dtos}/system-config-map.dto.ts | 0 .../system-config-new-version-check.dto.ts | 0 .../dto => dtos}/system-config-oauth.dto.ts | 0 .../system-config-password-login.dto.ts | 0 .../system-config-reverse-geocoding.dto.ts | 0 .../dto => dtos}/system-config-server.dto.ts | 0 .../system-config-storage-template.dto.ts | 11 ++ .../dto => dtos}/system-config-theme.dto.ts | 0 .../system-config-thumbnail.dto.ts | 0 .../dto => dtos}/system-config-trash.dto.ts | 0 .../dto => dtos}/system-config-user.dto.ts | 0 .../dto => dtos}/system-config.dto.ts | 32 ++-- .../tag-response.dto.ts => dtos/tag.dto.ts} | 19 ++ .../asset/dto => dtos}/time-bucket.dto.ts | 8 + server/src/dtos/user-profile.dto.ts | 28 +++ .../user.dto.spec.ts} | 16 +- server/src/dtos/user.dto.ts | 169 ++++++++++++++++++ .../immich/api-v1/asset/asset.controller.ts | 4 +- .../src/immich/api-v1/asset/asset.service.ts | 4 +- .../api-v1/asset/dto/create-asset.dto.ts | 2 +- .../interfaces/communication.repository.ts | 4 +- server/src/interfaces/library.repository.ts | 2 +- .../interfaces/machine-learning.repository.ts | 2 +- server/src/interfaces/storage.repository.ts | 2 +- server/src/middleware/auth.guard.ts | 2 +- .../src/middleware/file-upload.interceptor.ts | 3 +- .../repositories/filesystem.provider.spec.ts | 2 +- .../src/repositories/filesystem.provider.ts | 2 +- server/src/repositories/library.repository.ts | 2 +- .../machine-learning.repository.ts | 2 +- server/test/fixtures/auth.stub.ts | 2 +- server/test/fixtures/shared-link.stub.ts | 10 +- server/test/fixtures/tag.stub.ts | 2 +- 145 files changed, 917 insertions(+), 964 deletions(-) delete mode 100644 server/src/domain/album/dto/album-add-users.dto.ts delete mode 100644 server/src/domain/album/dto/album-create.dto.ts delete mode 100644 server/src/domain/album/dto/album-update.dto.ts delete mode 100644 server/src/domain/album/dto/album.dto.ts delete mode 100644 server/src/domain/album/dto/get-albums.dto.ts delete mode 100644 server/src/domain/asset/dto/asset-ids.dto.ts delete mode 100644 server/src/domain/asset/dto/asset-statistics.dto.ts delete mode 100644 server/src/domain/asset/dto/asset.dto.ts delete mode 100644 server/src/domain/asset/dto/map-marker.dto.ts delete mode 100644 server/src/domain/asset/dto/memory-lane.dto.ts delete mode 100644 server/src/domain/asset/response-dto/map-marker-response.dto.ts delete mode 100644 server/src/domain/asset/response-dto/smart-info-response.dto.ts delete mode 100644 server/src/domain/asset/response-dto/time-bucket-response.dto.ts delete mode 100644 server/src/domain/search/dto/search-suggestion.dto.ts delete mode 100644 server/src/domain/search/response-dto/search-explore.response.dto.ts delete mode 100644 server/src/domain/search/response-dto/search-response.dto.ts delete mode 100644 server/src/domain/shared-link/shared-link.dto.ts delete mode 100644 server/src/domain/system-config/response-dto/system-config-template-storage-option.dto.ts delete mode 100644 server/src/domain/tag/tag.dto.ts delete mode 100644 server/src/domain/user/dto/create-profile-image.dto.ts delete mode 100644 server/src/domain/user/dto/create-user.dto.ts delete mode 100644 server/src/domain/user/dto/delete-user.dto.ts delete mode 100644 server/src/domain/user/dto/update-user.dto.spec.ts delete mode 100644 server/src/domain/user/dto/update-user.dto.ts delete mode 100644 server/src/domain/user/response-dto/create-profile-image-response.dto.ts delete mode 100644 server/src/domain/user/response-dto/user-response.dto.ts rename server/src/{domain/activity => dtos}/activity.dto.ts (95%) rename server/src/{domain/album => dtos}/album-response.dto.spec.ts (89%) rename server/src/{domain/album/album-response.dto.ts => dtos/album.dto.ts} (59%) rename server/src/{domain/api-key => dtos}/api-key.dto.ts (100%) rename server/src/{domain/asset/response-dto/asset-ids-response.dto.ts => dtos/asset-ids.response.dto.ts} (100%) rename server/src/{domain/asset/response-dto => dtos}/asset-response.dto.ts (88%) create mode 100644 server/src/dtos/asset.dto.ts rename server/src/{domain/audit => dtos}/audit.dto.ts (100%) rename server/src/{domain/auth => dtos}/auth.dto.ts (100%) rename server/src/{domain/download => dtos}/download.dto.ts (100%) rename server/src/{domain/asset/response-dto/exif-response.dto.ts => dtos/exif.dto.ts} (100%) rename server/src/{domain/job => dtos}/job.dto.ts (100%) rename server/src/{domain/library => dtos}/library.dto.ts (100%) rename server/src/{domain/smart-info/dto => dtos}/model-config.dto.ts (100%) rename server/src/{domain/partner => dtos}/partner.dto.ts (71%) rename server/src/{domain/person => dtos}/person.dto.ts (98%) rename server/src/{domain/search/dto => dtos}/search.dto.ts (66%) rename server/src/{domain/server-info => dtos}/server-info.dto.ts (96%) rename server/src/{domain/shared-link/shared-link-response.dto.ts => dtos/shared-link.dto.ts} (57%) rename server/src/{domain/asset/dto/asset-stack.dto.ts => dtos/stack.dto.ts} (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-ffmpeg.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-job.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-library.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-logging.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-machine-learning.dto.ts (85%) rename server/src/{domain/system-config/dto => dtos}/system-config-map.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-new-version-check.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-oauth.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-password-login.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-reverse-geocoding.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-server.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-storage-template.dto.ts (53%) rename server/src/{domain/system-config/dto => dtos}/system-config-theme.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-thumbnail.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-trash.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config-user.dto.ts (100%) rename server/src/{domain/system-config/dto => dtos}/system-config.dto.ts (56%) rename server/src/{domain/tag/tag-response.dto.ts => dtos/tag.dto.ts} (54%) rename server/src/{domain/asset/dto => dtos}/time-bucket.dto.ts (88%) create mode 100644 server/src/dtos/user-profile.dto.ts rename server/src/{domain/user/dto/create-user.dto.spec.ts => dtos/user.dto.spec.ts} (79%) create mode 100644 server/src/dtos/user.dto.ts diff --git a/server/e2e/client/asset-api.ts b/server/e2e/client/asset-api.ts index 8f30e1f4a..f32d58611 100644 --- a/server/e2e/client/asset-api.ts +++ b/server/e2e/client/asset-api.ts @@ -1,4 +1,4 @@ -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import request from 'supertest'; export const assetApi = { diff --git a/server/e2e/client/auth-api.ts b/server/e2e/client/auth-api.ts index 46b21fb98..a8cfe4660 100644 --- a/server/e2e/client/auth-api.ts +++ b/server/e2e/client/auth-api.ts @@ -1,5 +1,5 @@ -import { LoginResponseDto } from 'src/domain/auth/auth.dto'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; +import { LoginResponseDto } from 'src/dtos/auth.dto'; +import { UserResponseDto } from 'src/dtos/user.dto'; import request from 'supertest'; import { adminSignupStub, loginResponseStub, loginStub } from 'test/fixtures/auth.stub'; diff --git a/server/e2e/client/library-api.ts b/server/e2e/client/library-api.ts index 90b1b7451..70c8c4c36 100644 --- a/server/e2e/client/library-api.ts +++ b/server/e2e/client/library-api.ts @@ -1,4 +1,4 @@ -import { CreateLibraryDto, LibraryResponseDto, ScanLibraryDto } from 'src/domain/library/library.dto'; +import { CreateLibraryDto, LibraryResponseDto, ScanLibraryDto } from 'src/dtos/library.dto'; import request from 'supertest'; export const libraryApi = { diff --git a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts index 3d43227b7..0a005c7d5 100644 --- a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts +++ b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts @@ -1,9 +1,9 @@ import { api } from 'e2e/client'; import fs from 'node:fs/promises'; import path from 'node:path'; -import { LoginResponseDto } from 'src/domain/auth/auth.dto'; -import { LibraryResponseDto } from 'src/domain/library/library.dto'; import { LibraryService } from 'src/domain/library/library.service'; +import { LoginResponseDto } from 'src/dtos/auth.dto'; +import { LibraryResponseDto } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; import { StorageEventType } from 'src/interfaces/storage.repository'; diff --git a/server/e2e/jobs/specs/library.e2e-spec.ts b/server/e2e/jobs/specs/library.e2e-spec.ts index 657c53fc2..3ae27e631 100644 --- a/server/e2e/jobs/specs/library.e2e-spec.ts +++ b/server/e2e/jobs/specs/library.e2e-spec.ts @@ -1,7 +1,7 @@ import { api } from 'e2e/client'; import fs from 'node:fs'; import { LibraryController } from 'src/controllers/library.controller'; -import { LoginResponseDto } from 'src/domain/auth/auth.dto'; +import { LoginResponseDto } from 'src/dtos/auth.dto'; import { LibraryType } from 'src/entities/library.entity'; import request from 'supertest'; import { errorStub } from 'test/fixtures/error.stub'; diff --git a/server/src/commands/reset-admin-password.command.ts b/server/src/commands/reset-admin-password.command.ts index ce0a897a8..b67ef418e 100644 --- a/server/src/commands/reset-admin-password.command.ts +++ b/server/src/commands/reset-admin-password.command.ts @@ -1,6 +1,6 @@ import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; import { UserService } from 'src/domain/user/user.service'; +import { UserResponseDto } from 'src/dtos/user.dto'; @Command({ name: 'reset-admin-password', diff --git a/server/src/controllers/activity.controller.ts b/server/src/controllers/activity.controller.ts index c405b0c6f..92e8797aa 100644 --- a/server/src/controllers/activity.controller.ts +++ b/server/src/controllers/activity.controller.ts @@ -1,15 +1,15 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Response } from 'express'; +import { ActivityService } from 'src/domain/activity/activity.service'; import { ActivityCreateDto, ActivityDto, ActivityResponseDto, ActivitySearchDto, ActivityStatisticsResponseDto, -} from 'src/domain/activity/activity.dto'; -import { ActivityService } from 'src/domain/activity/activity.service'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +} from 'src/dtos/activity.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index 18fd549b6..7cc39a860 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -1,14 +1,17 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AlbumCountResponseDto, AlbumResponseDto } from 'src/domain/album/album-response.dto'; import { AlbumService } from 'src/domain/album/album.service'; -import { AddUsersDto } from 'src/domain/album/dto/album-add-users.dto'; -import { CreateAlbumDto } from 'src/domain/album/dto/album-create.dto'; -import { UpdateAlbumDto } from 'src/domain/album/dto/album-update.dto'; -import { AlbumInfoDto } from 'src/domain/album/dto/album.dto'; -import { GetAlbumsDto } from 'src/domain/album/dto/get-albums.dto'; -import { BulkIdResponseDto, BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { + AddUsersDto, + AlbumCountResponseDto, + AlbumInfoDto, + AlbumResponseDto, + CreateAlbumDto, + GetAlbumsDto, + UpdateAlbumDto, +} from 'src/dtos/album.dto'; +import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { ParseMeUUIDPipe, UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/api-key.controller.ts b/server/src/controllers/api-key.controller.ts index 2d4f8db15..9b6838f8d 100644 --- a/server/src/controllers/api-key.controller.ts +++ b/server/src/controllers/api-key.controller.ts @@ -1,13 +1,8 @@ import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { - APIKeyCreateDto, - APIKeyCreateResponseDto, - APIKeyResponseDto, - APIKeyUpdateDto, -} from 'src/domain/api-key/api-key.dto'; import { APIKeyService } from 'src/domain/api-key/api-key.service'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index a6db59815..ca44aedfe 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -1,25 +1,22 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { AssetService } from 'src/domain/asset/asset.service'; -import { AssetJobsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { UpdateStackParentDto } from 'src/domain/asset/dto/asset-stack.dto'; -import { AssetStatsDto, AssetStatsResponseDto } from 'src/domain/asset/dto/asset-statistics.dto'; +import { SearchService } from 'src/domain/search/search.service'; +import { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto'; import { AssetBulkDeleteDto, AssetBulkUpdateDto, + AssetJobsDto, + AssetStatsDto, + AssetStatsResponseDto, DeviceIdDto, RandomAssetsDto, UpdateAssetDto, -} from 'src/domain/asset/dto/asset.dto'; -import { MapMarkerDto } from 'src/domain/asset/dto/map-marker.dto'; -import { MemoryLaneDto } from 'src/domain/asset/dto/memory-lane.dto'; -import { TimeBucketAssetDto, TimeBucketDto } from 'src/domain/asset/dto/time-bucket.dto'; -import { AssetResponseDto, MemoryLaneResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { MapMarkerResponseDto } from 'src/domain/asset/response-dto/map-marker-response.dto'; -import { TimeBucketResponseDto } from 'src/domain/asset/response-dto/time-bucket-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { MetadataSearchDto } from 'src/domain/search/dto/search.dto'; -import { SearchService } from 'src/domain/search/search.service'; +} from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto, MetadataSearchDto } from 'src/dtos/search.dto'; +import { UpdateStackParentDto } from 'src/dtos/stack.dto'; +import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Route } from 'src/middleware/file-upload.interceptor'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/audit.controller.ts b/server/src/controllers/audit.controller.ts index bb92f990e..b4d27b4ce 100644 --- a/server/src/controllers/audit.controller.ts +++ b/server/src/controllers/audit.controller.ts @@ -1,5 +1,6 @@ import { Body, Controller, Get, Post, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { AuditService } from 'src/domain/audit/audit.service'; import { AuditDeletesDto, AuditDeletesResponseDto, @@ -7,9 +8,8 @@ import { FileChecksumResponseDto, FileReportDto, FileReportFixDto, -} from 'src/domain/audit/audit.dto'; -import { AuditService } from 'src/domain/audit/audit.service'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +} from 'src/dtos/audit.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AdminRoute, Auth, Authenticated } from 'src/middleware/auth.guard'; @ApiTags('Audit') diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 941002fe6..6197c1e5f 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Req, import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/domain/auth/auth.constant'; +import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; import { AuthDeviceResponseDto, AuthDto, @@ -11,9 +12,8 @@ import { LogoutResponseDto, SignUpDto, ValidateAccessTokenResponseDto, -} from 'src/domain/auth/auth.dto'; -import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; -import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto'; +} from 'src/dtos/auth.dto'; +import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/download.controller.ts b/server/src/controllers/download.controller.ts index 0fb3520cf..8ba5d4281 100644 --- a/server/src/controllers/download.controller.ts +++ b/server/src/controllers/download.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, HttpCode, HttpStatus, Next, Param, Post, Res, StreamableFile } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { DownloadInfoDto, DownloadResponseDto } from 'src/domain/download/download.dto'; import { DownloadService } from 'src/domain/download/download.service'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; import { asStreamableFile, sendFile } from 'src/immich/app.utils'; import { Auth, Authenticated, FileResponse, SharedLinkRoute } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/face.controller.ts b/server/src/controllers/face.controller.ts index c7caba016..d5712d6b7 100644 --- a/server/src/controllers/face.controller.ts +++ b/server/src/controllers/face.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Get, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { AssetFaceResponseDto, FaceDto, PersonResponseDto } from 'src/domain/person/person.dto'; import { PersonService } from 'src/domain/person/person.service'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { AssetFaceResponseDto, FaceDto, PersonResponseDto } from 'src/dtos/person.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/job.controller.ts b/server/src/controllers/job.controller.ts index f0e9ed2be..04dea79ee 100644 --- a/server/src/controllers/job.controller.ts +++ b/server/src/controllers/job.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Get, Param, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AllJobStatusResponseDto, JobCommandDto, JobIdParamDto, JobStatusDto } from 'src/domain/job/job.dto'; import { JobService } from 'src/domain/job/job.service'; +import { AllJobStatusResponseDto, JobCommandDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto'; import { Authenticated } from 'src/middleware/auth.guard'; @ApiTags('Job') diff --git a/server/src/controllers/library.controller.ts b/server/src/controllers/library.controller.ts index 677773f2d..96c5f7415 100644 --- a/server/src/controllers/library.controller.ts +++ b/server/src/controllers/library.controller.ts @@ -1,5 +1,6 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { LibraryService } from 'src/domain/library/library.service'; import { CreateLibraryDto, LibraryResponseDto, @@ -9,8 +10,7 @@ import { UpdateLibraryDto, ValidateLibraryDto, ValidateLibraryResponseDto, -} from 'src/domain/library/library.dto'; -import { LibraryService } from 'src/domain/library/library.service'; +} from 'src/dtos/library.dto'; import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index 48b3eafd5..6da9faa17 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -1,15 +1,15 @@ import { Body, Controller, Get, HttpStatus, Post, Redirect, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; +import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; import { AuthDto, LoginResponseDto, OAuthAuthorizeResponseDto, OAuthCallbackDto, OAuthConfigDto, -} from 'src/domain/auth/auth.dto'; -import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; +} from 'src/dtos/auth.dto'; +import { UserResponseDto } from 'src/dtos/user.dto'; import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; @ApiTags('OAuth') diff --git a/server/src/controllers/partner.controller.ts b/server/src/controllers/partner.controller.ts index 8ca55e73c..c4f22a04f 100644 --- a/server/src/controllers/partner.controller.ts +++ b/server/src/controllers/partner.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; import { ApiQuery, ApiTags } from '@nestjs/swagger'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { PartnerResponseDto, UpdatePartnerDto } from 'src/domain/partner/partner.dto'; import { PartnerService } from 'src/domain/partner/partner.service'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { PartnerResponseDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; import { PartnerDirection } from 'src/interfaces/partner.repository'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index bc955fd2c..9c5602091 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -1,9 +1,10 @@ import { Body, Controller, Get, Next, Param, Post, Put, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { BulkIdResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { PersonService } from 'src/domain/person/person.service'; +import { BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFaceUpdateDto, MergePersonDto, @@ -14,8 +15,7 @@ import { PersonSearchDto, PersonStatisticsResponseDto, PersonUpdateDto, -} from 'src/domain/person/person.dto'; -import { PersonService } from 'src/domain/person/person.service'; +} from 'src/dtos/person.dto'; import { sendFile } from 'src/immich/app.utils'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 76fce1609..6b88f5ad8 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -1,20 +1,20 @@ import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { PersonResponseDto } from 'src/domain/person/person.dto'; -import { SearchSuggestionRequestDto } from 'src/domain/search/dto/search-suggestion.dto'; +import { SearchService } from 'src/domain/search/search.service'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { PersonResponseDto } from 'src/dtos/person.dto'; import { MetadataSearchDto, PlacesResponseDto, SearchDto, + SearchExploreResponseDto, SearchPeopleDto, SearchPlacesDto, + SearchResponseDto, + SearchSuggestionRequestDto, SmartSearchDto, -} from 'src/domain/search/dto/search.dto'; -import { SearchExploreResponseDto } from 'src/domain/search/response-dto/search-explore.response.dto'; -import { SearchResponseDto } from 'src/domain/search/response-dto/search-response.dto'; -import { SearchService } from 'src/domain/search/search.service'; +} from 'src/dtos/search.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; @ApiTags('Search') diff --git a/server/src/controllers/server-info.controller.ts b/server/src/controllers/server-info.controller.ts index aae617493..9937acde8 100644 --- a/server/src/controllers/server-info.controller.ts +++ b/server/src/controllers/server-info.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { ServerInfoService } from 'src/domain/server-info/server-info.service'; import { ServerConfigDto, ServerFeaturesDto, @@ -9,8 +10,7 @@ import { ServerStatsResponseDto, ServerThemeDto, ServerVersionResponseDto, -} from 'src/domain/server-info/server-info.dto'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; +} from 'src/dtos/server-info.dto'; import { AdminRoute, Authenticated, PublicRoute } from 'src/middleware/auth.guard'; @ApiTags('Server Info') diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 1e03a5c42..90bbd6ee1 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -1,13 +1,17 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AssetIdsResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/domain/auth/auth.constant'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { SharedLinkResponseDto } from 'src/domain/shared-link/shared-link-response.dto'; -import { SharedLinkCreateDto, SharedLinkEditDto, SharedLinkPasswordDto } from 'src/domain/shared-link/shared-link.dto'; import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; +import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { + SharedLinkCreateDto, + SharedLinkEditDto, + SharedLinkPasswordDto, + SharedLinkResponseDto, +} from 'src/dtos/shared-link.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/system-config.controller.ts b/server/src/controllers/system-config.controller.ts index d10bccee0..34f9f2e0e 100644 --- a/server/src/controllers/system-config.controller.ts +++ b/server/src/controllers/system-config.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Get, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { SystemConfigDto } from 'src/domain/system-config/dto/system-config.dto'; -import { SystemConfigTemplateStorageOptionDto } from 'src/domain/system-config/response-dto/system-config-template-storage-option.dto'; import { MapThemeDto } from 'src/domain/system-config/system-config-map-theme.dto'; import { SystemConfigService } from 'src/domain/system-config/system-config.service'; +import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config-storage-template.dto'; +import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; @ApiTags('System Config') diff --git a/server/src/controllers/tag.controller.ts b/server/src/controllers/tag.controller.ts index e914e577e..bbca4a05d 100644 --- a/server/src/controllers/tag.controller.ts +++ b/server/src/controllers/tag.controller.ts @@ -1,12 +1,11 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AssetIdsResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { TagResponseDto } from 'src/domain/tag/tag-response.dto'; -import { CreateTagDto, UpdateTagDto } from 'src/domain/tag/tag.dto'; import { TagService } from 'src/domain/tag/tag.service'; +import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { CreateTagDto, TagResponseDto, UpdateTagDto } from 'src/dtos/tag.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { UUIDParamDto } from 'src/validation'; diff --git a/server/src/controllers/trash.controller.ts b/server/src/controllers/trash.controller.ts index 2c0b0c946..5ea692d48 100644 --- a/server/src/controllers/trash.controller.ts +++ b/server/src/controllers/trash.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { TrashService } from 'src/domain/trash/trash.service'; +import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; @ApiTags('Trash') diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts index e573584ad..9fc0ff320 100644 --- a/server/src/controllers/user.controller.ts +++ b/server/src/controllers/user.controller.ts @@ -16,14 +16,10 @@ import { } from '@nestjs/common'; import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { CreateProfileImageDto } from 'src/domain/user/dto/create-profile-image.dto'; -import { CreateUserDto } from 'src/domain/user/dto/create-user.dto'; -import { DeleteUserDto } from 'src/domain/user/dto/delete-user.dto'; -import { UpdateUserDto } from 'src/domain/user/dto/update-user.dto'; -import { CreateProfileImageResponseDto } from 'src/domain/user/response-dto/create-profile-image-response.dto'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; import { UserService } from 'src/domain/user/user.service'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; +import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto } from 'src/dtos/user.dto'; import { sendFile } from 'src/immich/app.utils'; import { AdminRoute, Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor'; diff --git a/server/src/cores/access.core.ts b/server/src/cores/access.core.ts index f9e22bb6b..1c0b1dabb 100644 --- a/server/src/cores/access.core.ts +++ b/server/src/cores/access.core.ts @@ -1,5 +1,5 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { setDifference, setIsEqual, setUnion } from 'src/utils'; diff --git a/server/src/cores/system-config.core.ts b/server/src/cores/system-config.core.ts index 295a0e5ed..a6692fa18 100644 --- a/server/src/cores/system-config.core.ts +++ b/server/src/cores/system-config.core.ts @@ -6,7 +6,7 @@ import { load as loadYaml } from 'js-yaml'; import * as _ from 'lodash'; import { Subject } from 'rxjs'; import { QueueName } from 'src/domain/job/job.constants'; -import { SystemConfigDto } from 'src/domain/system-config/dto/system-config.dto'; +import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AudioCodec, CQMode, diff --git a/server/src/cores/user.core.ts b/server/src/cores/user.core.ts index d91f2bae5..1640e8d07 100644 --- a/server/src/cores/user.core.ts +++ b/server/src/cores/user.core.ts @@ -1,6 +1,6 @@ import { BadRequestException, ForbiddenException } from '@nestjs/common'; import sanitize from 'sanitize-filename'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; +import { UserResponseDto } from 'src/dtos/user.dto'; import { LibraryType } from 'src/entities/library.entity'; import { UserEntity } from 'src/entities/user.entity'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; diff --git a/server/src/domain/activity/activity.service.ts b/server/src/domain/activity/activity.service.ts index 81d77fa8d..ff310cb5d 100644 --- a/server/src/domain/activity/activity.service.ts +++ b/server/src/domain/activity/activity.service.ts @@ -10,8 +10,8 @@ import { ReactionLevel, ReactionType, mapActivity, -} from 'src/domain/activity/activity.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +} from 'src/dtos/activity.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { ActivityEntity } from 'src/entities/activity.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IActivityRepository } from 'src/interfaces/activity.repository'; diff --git a/server/src/domain/activity/activity.spec.ts b/server/src/domain/activity/activity.spec.ts index 467540be3..6013f0ff0 100644 --- a/server/src/domain/activity/activity.spec.ts +++ b/server/src/domain/activity/activity.spec.ts @@ -1,6 +1,6 @@ import { BadRequestException } from '@nestjs/common'; -import { ReactionType } from 'src/domain/activity/activity.dto'; import { ActivityService } from 'src/domain/activity/activity.service'; +import { ReactionType } from 'src/dtos/activity.dto'; import { IActivityRepository } from 'src/interfaces/activity.repository'; import { activityStub } from 'test/fixtures/activity.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/domain/album/album.service.spec.ts b/server/src/domain/album/album.service.spec.ts index 5b3a91ecf..41b505ea6 100644 --- a/server/src/domain/album/album.service.spec.ts +++ b/server/src/domain/album/album.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import _ from 'lodash'; import { AlbumService } from 'src/domain/album/album.service'; -import { BulkIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto'; +import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; diff --git a/server/src/domain/album/album.service.ts b/server/src/domain/album/album.service.ts index 4d459ea44..ea7178bed 100644 --- a/server/src/domain/album/album.service.ts +++ b/server/src/domain/album/album.service.ts @@ -1,19 +1,19 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; import { + AddUsersDto, AlbumCountResponseDto, + AlbumInfoDto, AlbumResponseDto, + CreateAlbumDto, + GetAlbumsDto, + UpdateAlbumDto, mapAlbum, mapAlbumWithAssets, mapAlbumWithoutAssets, -} from 'src/domain/album/album-response.dto'; -import { AddUsersDto } from 'src/domain/album/dto/album-add-users.dto'; -import { CreateAlbumDto } from 'src/domain/album/dto/album-create.dto'; -import { UpdateAlbumDto } from 'src/domain/album/dto/album-update.dto'; -import { AlbumInfoDto } from 'src/domain/album/dto/album.dto'; -import { GetAlbumsDto } from 'src/domain/album/dto/get-albums.dto'; -import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +} from 'src/dtos/album.dto'; +import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AlbumEntity } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { UserEntity } from 'src/entities/user.entity'; diff --git a/server/src/domain/album/dto/album-add-users.dto.ts b/server/src/domain/album/dto/album-add-users.dto.ts deleted file mode 100644 index 1a6be4823..000000000 --- a/server/src/domain/album/dto/album-add-users.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ArrayNotEmpty } from 'class-validator'; -import { ValidateUUID } from 'src/validation'; - -export class AddUsersDto { - @ValidateUUID({ each: true }) - @ArrayNotEmpty() - sharedUserIds!: string[]; -} diff --git a/server/src/domain/album/dto/album-create.dto.ts b/server/src/domain/album/dto/album-create.dto.ts deleted file mode 100644 index 1b4a75332..000000000 --- a/server/src/domain/album/dto/album-create.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; -import { Optional, ValidateUUID } from 'src/validation'; - -export class CreateAlbumDto { - @IsString() - @ApiProperty() - albumName!: string; - - @IsString() - @Optional() - description?: string; - - @ValidateUUID({ optional: true, each: true }) - sharedWithUserIds?: string[]; - - @ValidateUUID({ optional: true, each: true }) - assetIds?: string[]; -} diff --git a/server/src/domain/album/dto/album-update.dto.ts b/server/src/domain/album/dto/album-update.dto.ts deleted file mode 100644 index cf2d5f8c2..000000000 --- a/server/src/domain/album/dto/album-update.dto.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsString } from 'class-validator'; -import { AssetOrder } from 'src/entities/album.entity'; -import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; - -export class UpdateAlbumDto { - @Optional() - @IsString() - albumName?: string; - - @Optional() - @IsString() - description?: string; - - @ValidateUUID({ optional: true }) - albumThumbnailAssetId?: string; - - @ValidateBoolean({ optional: true }) - isActivityEnabled?: boolean; - - @IsEnum(AssetOrder) - @Optional() - @ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' }) - order?: AssetOrder; -} diff --git a/server/src/domain/album/dto/album.dto.ts b/server/src/domain/album/dto/album.dto.ts deleted file mode 100644 index fe0eb0d5c..000000000 --- a/server/src/domain/album/dto/album.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ValidateBoolean } from 'src/validation'; - -export class AlbumInfoDto { - @ValidateBoolean({ optional: true }) - withoutAssets?: boolean; -} diff --git a/server/src/domain/album/dto/get-albums.dto.ts b/server/src/domain/album/dto/get-albums.dto.ts deleted file mode 100644 index 15e4f1cf2..000000000 --- a/server/src/domain/album/dto/get-albums.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ValidateBoolean, ValidateUUID } from 'src/validation'; - -export class GetAlbumsDto { - @ValidateBoolean({ optional: true }) - /** - * true: only shared albums - * false: only non-shared own albums - * undefined: shared and owned albums - */ - shared?: boolean; - - /** - * Only returns albums that contain the asset - * Ignores the shared parameter - * undefined: get all albums - */ - @ValidateUUID({ optional: true }) - assetId?: string; -} diff --git a/server/src/domain/api-key/api-key.service.ts b/server/src/domain/api-key/api-key.service.ts index 540c1d71f..6c874895b 100644 --- a/server/src/domain/api-key/api-key.service.ts +++ b/server/src/domain/api-key/api-key.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto } from 'src/domain/api-key/api-key.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto } from 'src/dtos/api-key.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { APIKeyEntity } from 'src/entities/api-key.entity'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; diff --git a/server/src/domain/asset/asset.service.spec.ts b/server/src/domain/asset/asset.service.spec.ts index 7b49cfef9..a3603c3da 100644 --- a/server/src/domain/asset/asset.service.spec.ts +++ b/server/src/domain/asset/asset.service.spec.ts @@ -1,10 +1,9 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; import { when } from 'jest-when'; -import { AssetService, UploadFieldName } from 'src/domain/asset/asset.service'; -import { AssetJobName } from 'src/domain/asset/dto/asset-ids.dto'; -import { AssetStatsResponseDto } from 'src/domain/asset/dto/asset-statistics.dto'; -import { mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; +import { AssetService } from 'src/domain/asset/asset.service'; import { JobName } from 'src/domain/job/job.constants'; +import { mapAsset } from 'src/dtos/asset-response.dto'; +import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/asset.dto'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository'; import { AssetStats, IAssetRepository, TimeBucketSize } from 'src/interfaces/asset.repository'; diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts index 38446b576..51a587a67 100644 --- a/server/src/domain/asset/asset.service.ts +++ b/server/src/domain/asset/asset.service.ts @@ -6,25 +6,29 @@ import sanitize from 'sanitize-filename'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { AssetJobName, AssetJobsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { UpdateStackParentDto } from 'src/domain/asset/dto/asset-stack.dto'; -import { AssetStatsDto, mapStats } from 'src/domain/asset/dto/asset-statistics.dto'; -import { AssetBulkDeleteDto, AssetBulkUpdateDto, UpdateAssetDto } from 'src/domain/asset/dto/asset.dto'; -import { MapMarkerDto } from 'src/domain/asset/dto/map-marker.dto'; -import { MemoryLaneDto } from 'src/domain/asset/dto/memory-lane.dto'; -import { TimeBucketAssetDto, TimeBucketDto } from 'src/domain/asset/dto/time-bucket.dto'; +import { mimeTypes } from 'src/domain/domain.constant'; +import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; +import { IAssetDeletionJob, ISidecarWriteJob } from 'src/domain/job/job.interface'; import { AssetResponseDto, MemoryLaneResponseDto, SanitizedAssetResponseDto, mapAsset, -} from 'src/domain/asset/response-dto/asset-response.dto'; -import { MapMarkerResponseDto } from 'src/domain/asset/response-dto/map-marker-response.dto'; -import { TimeBucketResponseDto } from 'src/domain/asset/response-dto/time-bucket-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { mimeTypes } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; -import { IAssetDeletionJob, ISidecarWriteJob } from 'src/domain/job/job.interface'; +} from 'src/dtos/asset-response.dto'; +import { + AssetBulkDeleteDto, + AssetBulkUpdateDto, + AssetJobName, + AssetJobsDto, + AssetStatsDto, + UpdateAssetDto, + UploadFieldName, + mapStats, +} from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { MapMarkerDto, MapMarkerResponseDto, MemoryLaneDto } from 'src/dtos/search.dto'; +import { UpdateStackParentDto } from 'src/dtos/stack.dto'; +import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; import { ImmichLogger } from 'src/infra/logger'; @@ -39,13 +43,6 @@ import { ISystemConfigRepository } from 'src/interfaces/system-config.repository import { IUserRepository } from 'src/interfaces/user.repository'; import { usePagination } from 'src/utils'; -export enum UploadFieldName { - ASSET_DATA = 'assetData', - LIVE_PHOTO_DATA = 'livePhotoData', - SIDECAR_DATA = 'sidecarData', - PROFILE_DATA = 'file', -} - export interface UploadRequest { auth: AuthDto | null; fieldName: UploadFieldName; diff --git a/server/src/domain/asset/dto/asset-ids.dto.ts b/server/src/domain/asset/dto/asset-ids.dto.ts deleted file mode 100644 index ea875e85e..000000000 --- a/server/src/domain/asset/dto/asset-ids.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum } from 'class-validator'; -import { ValidateUUID } from 'src/validation'; - -export class AssetIdsDto { - @ValidateUUID({ each: true }) - assetIds!: string[]; -} - -export enum AssetJobName { - REGENERATE_THUMBNAIL = 'regenerate-thumbnail', - REFRESH_METADATA = 'refresh-metadata', - TRANSCODE_VIDEO = 'transcode-video', -} - -export class AssetJobsDto extends AssetIdsDto { - @ApiProperty({ enumName: 'AssetJobName', enum: AssetJobName }) - @IsEnum(AssetJobName) - name!: AssetJobName; -} diff --git a/server/src/domain/asset/dto/asset-statistics.dto.ts b/server/src/domain/asset/dto/asset-statistics.dto.ts deleted file mode 100644 index 93e007029..000000000 --- a/server/src/domain/asset/dto/asset-statistics.dto.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { AssetType } from 'src/entities/asset.entity'; -import { AssetStats } from 'src/interfaces/asset.repository'; -import { ValidateBoolean } from 'src/validation'; - -export class AssetStatsDto { - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - - @ValidateBoolean({ optional: true }) - isFavorite?: boolean; - - @ValidateBoolean({ optional: true }) - isTrashed?: boolean; -} - -export class AssetStatsResponseDto { - @ApiProperty({ type: 'integer' }) - images!: number; - - @ApiProperty({ type: 'integer' }) - videos!: number; - - @ApiProperty({ type: 'integer' }) - total!: number; -} - -export const mapStats = (stats: AssetStats): AssetStatsResponseDto => { - return { - images: stats[AssetType.IMAGE], - videos: stats[AssetType.VIDEO], - total: Object.values(stats).reduce((total, value) => total + value, 0), - }; -}; diff --git a/server/src/domain/asset/dto/asset.dto.ts b/server/src/domain/asset/dto/asset.dto.ts deleted file mode 100644 index a93a59ae3..000000000 --- a/server/src/domain/asset/dto/asset.dto.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Type } from 'class-transformer'; -import { - IsDateString, - IsInt, - IsLatitude, - IsLongitude, - IsNotEmpty, - IsPositive, - IsString, - ValidateIf, -} from 'class-validator'; -import { BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; - -export class DeviceIdDto { - @IsNotEmpty() - @IsString() - deviceId!: string; -} - -const hasGPS = (o: { latitude: undefined; longitude: undefined }) => - o.latitude !== undefined || o.longitude !== undefined; -const ValidateGPS = () => ValidateIf(hasGPS); - -export class UpdateAssetBase { - @ValidateBoolean({ optional: true }) - isFavorite?: boolean; - - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - - @Optional() - @IsDateString() - dateTimeOriginal?: string; - - @ValidateGPS() - @IsLatitude() - @IsNotEmpty() - latitude?: number; - - @ValidateGPS() - @IsLongitude() - @IsNotEmpty() - longitude?: number; -} - -export class AssetBulkUpdateDto extends UpdateAssetBase { - @ValidateUUID({ each: true }) - ids!: string[]; - - @ValidateUUID({ optional: true }) - stackParentId?: string; - - @ValidateBoolean({ optional: true }) - removeParent?: boolean; -} - -export class UpdateAssetDto extends UpdateAssetBase { - @Optional() - @IsString() - description?: string; -} - -export class RandomAssetsDto { - @Optional() - @IsInt() - @IsPositive() - @Type(() => Number) - count?: number; -} - -export class AssetBulkDeleteDto extends BulkIdsDto { - @ValidateBoolean({ optional: true }) - force?: boolean; -} diff --git a/server/src/domain/asset/dto/map-marker.dto.ts b/server/src/domain/asset/dto/map-marker.dto.ts deleted file mode 100644 index 158750e51..000000000 --- a/server/src/domain/asset/dto/map-marker.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ValidateBoolean, ValidateDate } from 'src/validation'; - -export class MapMarkerDto { - @ValidateBoolean({ optional: true }) - isArchived?: boolean; - - @ValidateBoolean({ optional: true }) - isFavorite?: boolean; - - @ValidateDate({ optional: true }) - fileCreatedAfter?: Date; - - @ValidateDate({ optional: true }) - fileCreatedBefore?: Date; - - @ValidateBoolean({ optional: true }) - withPartners?: boolean; -} diff --git a/server/src/domain/asset/dto/memory-lane.dto.ts b/server/src/domain/asset/dto/memory-lane.dto.ts deleted file mode 100644 index 43f74aff1..000000000 --- a/server/src/domain/asset/dto/memory-lane.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsInt, Max, Min } from 'class-validator'; - -export class MemoryLaneDto { - @IsInt() - @Type(() => Number) - @Max(31) - @Min(1) - @ApiProperty({ type: 'integer' }) - day!: number; - - @IsInt() - @Type(() => Number) - @Max(12) - @Min(1) - @ApiProperty({ type: 'integer' }) - month!: number; -} diff --git a/server/src/domain/asset/response-dto/map-marker-response.dto.ts b/server/src/domain/asset/response-dto/map-marker-response.dto.ts deleted file mode 100644 index f5148883f..000000000 --- a/server/src/domain/asset/response-dto/map-marker-response.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class MapMarkerResponseDto { - @ApiProperty() - id!: string; - - @ApiProperty({ format: 'double' }) - lat!: number; - - @ApiProperty({ format: 'double' }) - lon!: number; - - @ApiProperty() - city!: string | null; - - @ApiProperty() - state!: string | null; - - @ApiProperty() - country!: string | null; -} diff --git a/server/src/domain/asset/response-dto/smart-info-response.dto.ts b/server/src/domain/asset/response-dto/smart-info-response.dto.ts deleted file mode 100644 index c55c7ffb3..000000000 --- a/server/src/domain/asset/response-dto/smart-info-response.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SmartInfoEntity } from 'src/entities/smart-info.entity'; - -export class SmartInfoResponseDto { - tags?: string[] | null; - objects?: string[] | null; -} - -export function mapSmartInfo(entity: SmartInfoEntity): SmartInfoResponseDto { - return { - tags: entity.tags, - objects: entity.objects, - }; -} diff --git a/server/src/domain/asset/response-dto/time-bucket-response.dto.ts b/server/src/domain/asset/response-dto/time-bucket-response.dto.ts deleted file mode 100644 index e143dde46..000000000 --- a/server/src/domain/asset/response-dto/time-bucket-response.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class TimeBucketResponseDto { - @ApiProperty({ type: 'string' }) - timeBucket!: string; - - @ApiProperty({ type: 'integer' }) - count!: number; -} diff --git a/server/src/domain/audit/audit.service.ts b/server/src/domain/audit/audit.service.ts index 91acb54d9..b84f3824d 100644 --- a/server/src/domain/audit/audit.service.ts +++ b/server/src/domain/audit/audit.service.ts @@ -3,6 +3,8 @@ import { DateTime } from 'luxon'; import { resolve } from 'node:path'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; +import { AUDIT_LOG_MAX_DURATION } from 'src/domain/domain.constant'; +import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants'; import { AuditDeletesDto, AuditDeletesResponseDto, @@ -10,10 +12,8 @@ import { FileChecksumResponseDto, FileReportItemDto, PathEntityType, -} from 'src/domain/audit/audit.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { AUDIT_LOG_MAX_DURATION } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants'; +} from 'src/dtos/audit.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { DatabaseAction } from 'src/entities/audit.entity'; import { AssetPathType, PersonPathType, UserPathType } from 'src/entities/move.entity'; import { ImmichLogger } from 'src/infra/logger'; diff --git a/server/src/domain/auth/auth.service.spec.ts b/server/src/domain/auth/auth.service.spec.ts index e091cdcd9..37c759937 100644 --- a/server/src/domain/auth/auth.service.spec.ts +++ b/server/src/domain/auth/auth.service.spec.ts @@ -3,8 +3,8 @@ import { IncomingHttpHeaders } from 'node:http'; import { Issuer, generators } from 'openid-client'; import { Socket } from 'socket.io'; import { AuthType } from 'src/domain/auth/auth.constant'; -import { AuthDto, SignUpDto } from 'src/domain/auth/auth.dto'; import { AuthService } from 'src/domain/auth/auth.service'; +import { AuthDto, SignUpDto } from 'src/dtos/auth.dto'; import { UserEntity } from 'src/entities/user.entity'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; diff --git a/server/src/domain/auth/auth.service.ts b/server/src/domain/auth/auth.service.ts index 2d807a249..9ae41a4fc 100644 --- a/server/src/domain/auth/auth.service.ts +++ b/server/src/domain/auth/auth.service.ts @@ -35,8 +35,8 @@ import { SignUpDto, mapLoginResponse, mapUserToken, -} from 'src/domain/auth/auth.dto'; -import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto'; +} from 'src/dtos/auth.dto'; +import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { SystemConfig } from 'src/entities/system-config.entity'; import { UserEntity } from 'src/entities/user.entity'; import { ImmichLogger } from 'src/infra/logger'; diff --git a/server/src/domain/download/download.service.spec.ts b/server/src/domain/download/download.service.spec.ts index 6e1eafbec..cfdff82b9 100644 --- a/server/src/domain/download/download.service.spec.ts +++ b/server/src/domain/download/download.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { when } from 'jest-when'; -import { DownloadResponseDto } from 'src/domain/download/download.dto'; import { DownloadService } from 'src/domain/download/download.service'; +import { DownloadResponseDto } from 'src/dtos/download.dto'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { CacheControl, ImmichFileResponse } from 'src/utils'; diff --git a/server/src/domain/download/download.service.ts b/server/src/domain/download/download.service.ts index 8c0168a6c..b03951764 100644 --- a/server/src/domain/download/download.service.ts +++ b/server/src/domain/download/download.service.ts @@ -1,10 +1,10 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { parse } from 'node:path'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { mimeTypes } from 'src/domain/domain.constant'; -import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/domain/download/download.dto'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; diff --git a/server/src/domain/job/job.service.ts b/server/src/domain/job/job.service.ts index 192b093c1..8650b92d4 100644 --- a/server/src/domain/job/job.service.ts +++ b/server/src/domain/job/job.service.ts @@ -1,8 +1,8 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; import { ConcurrentQueueName, JobCommand, JobName, QueueName } from 'src/domain/job/job.constants'; -import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto } from 'src/domain/job/job.dto'; +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 { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository } from 'src/interfaces/asset.repository'; diff --git a/server/src/domain/library/library.service.spec.ts b/server/src/domain/library/library.service.spec.ts index f34b6d5be..81bf1cc86 100644 --- a/server/src/domain/library/library.service.spec.ts +++ b/server/src/domain/library/library.service.spec.ts @@ -5,8 +5,8 @@ import { Stats } from 'node:fs'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { JobName } from 'src/domain/job/job.constants'; import { ILibraryFileJob, ILibraryRefreshJob } from 'src/domain/job/job.interface'; -import { mapLibrary } from 'src/domain/library/library.dto'; import { LibraryService } from 'src/domain/library/library.service'; +import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index 1fc009149..f7b298797 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -22,7 +22,7 @@ import { ValidateLibraryImportPathResponseDto, ValidateLibraryResponseDto, mapLibrary, -} from 'src/domain/library/library.dto'; +} from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; import { ImmichLogger } from 'src/infra/logger'; diff --git a/server/src/domain/media/media.service.ts b/server/src/domain/media/media.service.ts index c254689b4..5c217f2a8 100644 --- a/server/src/domain/media/media.service.ts +++ b/server/src/domain/media/media.service.ts @@ -13,7 +13,7 @@ import { VAAPIConfig, VP9Config, } from 'src/domain/media/media.util'; -import { SystemConfigFFmpegDto } from 'src/domain/system-config/dto/system-config-ffmpeg.dto'; +import { SystemConfigFFmpegDto } from 'src/dtos/system-config-ffmpeg.dto'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; import { diff --git a/server/src/domain/media/media.util.ts b/server/src/domain/media/media.util.ts index 3e58ca81a..d6a5b53a6 100644 --- a/server/src/domain/media/media.util.ts +++ b/server/src/domain/media/media.util.ts @@ -1,4 +1,4 @@ -import { SystemConfigFFmpegDto } from 'src/domain/system-config/dto/system-config-ffmpeg.dto'; +import { SystemConfigFFmpegDto } from 'src/dtos/system-config-ffmpeg.dto'; import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity'; import { AudioStreamInfo, diff --git a/server/src/domain/partner/partner.service.spec.ts b/server/src/domain/partner/partner.service.spec.ts index 7d4821282..1256a9ca0 100644 --- a/server/src/domain/partner/partner.service.spec.ts +++ b/server/src/domain/partner/partner.service.spec.ts @@ -1,6 +1,6 @@ import { BadRequestException } from '@nestjs/common'; -import { PartnerResponseDto } from 'src/domain/partner/partner.dto'; import { PartnerService } from 'src/domain/partner/partner.service'; +import { PartnerResponseDto } from 'src/dtos/partner.dto'; import { UserAvatarColor } from 'src/entities/user.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IPartnerRepository, PartnerDirection } from 'src/interfaces/partner.repository'; diff --git a/server/src/domain/partner/partner.service.ts b/server/src/domain/partner/partner.service.ts index 34a88a7ea..af3f98061 100644 --- a/server/src/domain/partner/partner.service.ts +++ b/server/src/domain/partner/partner.service.ts @@ -1,8 +1,8 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { PartnerResponseDto, UpdatePartnerDto } from 'src/domain/partner/partner.dto'; -import { mapUser } from 'src/domain/user/response-dto/user-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { PartnerResponseDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; +import { mapUser } from 'src/dtos/user.dto'; import { PartnerEntity } from 'src/entities/partner.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IPartnerRepository, PartnerDirection, PartnerIds } from 'src/interfaces/partner.repository'; diff --git a/server/src/domain/person/person.service.spec.ts b/server/src/domain/person/person.service.spec.ts index 9da66e868..3f555e34f 100644 --- a/server/src/domain/person/person.service.spec.ts +++ b/server/src/domain/person/person.service.spec.ts @@ -1,8 +1,8 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; -import { BulkIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto'; import { JobName } from 'src/domain/job/job.constants'; -import { PersonResponseDto, mapFaces, mapPerson } from 'src/domain/person/person.dto'; import { PersonService } from 'src/domain/person/person.service'; +import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; +import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { Colorspace, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; diff --git a/server/src/domain/person/person.service.ts b/server/src/domain/person/person.service.ts index cd7ba9224..7da482966 100644 --- a/server/src/domain/person/person.service.ts +++ b/server/src/domain/person/person.service.ts @@ -2,13 +2,13 @@ import { BadRequestException, Inject, Injectable, NotFoundException } from '@nes import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { BulkIdErrorReason, BulkIdResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { mimeTypes } from 'src/domain/domain.constant'; import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants'; import { IBaseJob, IDeferrableJob, IEntityJob } from 'src/domain/job/job.interface'; import { FACE_THUMBNAIL_SIZE } from 'src/domain/media/media.constant'; +import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFaceResponseDto, AssetFaceUpdateDto, @@ -23,7 +23,7 @@ import { PersonUpdateDto, mapFaces, mapPerson, -} from 'src/domain/person/person.dto'; +} from 'src/dtos/person.dto'; import { PersonPathType } from 'src/entities/move.entity'; import { PersonEntity } from 'src/entities/person.entity'; import { ImmichLogger } from 'src/infra/logger'; diff --git a/server/src/domain/search/dto/search-suggestion.dto.ts b/server/src/domain/search/dto/search-suggestion.dto.ts deleted file mode 100644 index f702293d0..000000000 --- a/server/src/domain/search/dto/search-suggestion.dto.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Optional } from 'src/validation'; - -export enum SearchSuggestionType { - COUNTRY = 'country', - STATE = 'state', - CITY = 'city', - CAMERA_MAKE = 'camera-make', - CAMERA_MODEL = 'camera-model', -} - -export class SearchSuggestionRequestDto { - @IsEnum(SearchSuggestionType) - @IsNotEmpty() - @ApiProperty({ enumName: 'SearchSuggestionType', enum: SearchSuggestionType }) - type!: SearchSuggestionType; - - @IsString() - @Optional() - country?: string; - - @IsString() - @Optional() - state?: string; - - @IsString() - @Optional() - make?: string; - - @IsString() - @Optional() - model?: string; -} diff --git a/server/src/domain/search/response-dto/search-explore.response.dto.ts b/server/src/domain/search/response-dto/search-explore.response.dto.ts deleted file mode 100644 index 33689b979..000000000 --- a/server/src/domain/search/response-dto/search-explore.response.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; - -class SearchExploreItem { - value!: string; - data!: AssetResponseDto; -} - -export class SearchExploreResponseDto { - fieldName!: string; - items!: SearchExploreItem[]; -} diff --git a/server/src/domain/search/response-dto/search-response.dto.ts b/server/src/domain/search/response-dto/search-response.dto.ts deleted file mode 100644 index 53563a9ac..000000000 --- a/server/src/domain/search/response-dto/search-response.dto.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { AlbumResponseDto } from 'src/domain/album/album-response.dto'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; - -class SearchFacetCountResponseDto { - @ApiProperty({ type: 'integer' }) - count!: number; - value!: string; -} - -class SearchFacetResponseDto { - fieldName!: string; - counts!: SearchFacetCountResponseDto[]; -} - -class SearchAlbumResponseDto { - @ApiProperty({ type: 'integer' }) - total!: number; - @ApiProperty({ type: 'integer' }) - count!: number; - items!: AlbumResponseDto[]; - facets!: SearchFacetResponseDto[]; -} - -class SearchAssetResponseDto { - @ApiProperty({ type: 'integer' }) - total!: number; - @ApiProperty({ type: 'integer' }) - count!: number; - items!: AssetResponseDto[]; - facets!: SearchFacetResponseDto[]; - nextPage!: string | null; -} - -export class SearchResponseDto { - albums!: SearchAlbumResponseDto; - assets!: SearchAssetResponseDto; -} diff --git a/server/src/domain/search/search.service.spec.ts b/server/src/domain/search/search.service.spec.ts index f72212809..7f83f2ff0 100644 --- a/server/src/domain/search/search.service.spec.ts +++ b/server/src/domain/search/search.service.spec.ts @@ -1,6 +1,6 @@ -import { mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { SearchDto } from 'src/domain/search/dto/search.dto'; import { SearchService } from 'src/domain/search/search.service'; +import { mapAsset } from 'src/dtos/asset-response.dto'; +import { SearchDto } from 'src/dtos/search.dto'; import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; diff --git a/server/src/domain/search/search.service.ts b/server/src/domain/search/search.service.ts index cedee68d0..30819dc46 100644 --- a/server/src/domain/search/search.service.ts +++ b/server/src/domain/search/search.service.ts @@ -1,19 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { PersonResponseDto } from 'src/domain/person/person.dto'; -import { SearchSuggestionRequestDto, SearchSuggestionType } from 'src/domain/search/dto/search-suggestion.dto'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { PersonResponseDto } from 'src/dtos/person.dto'; import { MetadataSearchDto, PlacesResponseDto, SearchDto, SearchPeopleDto, SearchPlacesDto, + SearchResponseDto, + SearchSuggestionRequestDto, + SearchSuggestionType, SmartSearchDto, mapPlaces, -} from 'src/domain/search/dto/search.dto'; -import { SearchResponseDto } from 'src/domain/search/response-dto/search-response.dto'; +} from 'src/dtos/search.dto'; import { AssetOrder } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; diff --git a/server/src/domain/server-info/server-info.service.ts b/server/src/domain/server-info/server-info.service.ts index 13921949c..a76ae4022 100644 --- a/server/src/domain/server-info/server-info.service.ts +++ b/server/src/domain/server-info/server-info.service.ts @@ -11,7 +11,7 @@ import { ServerPingResponse, ServerStatsResponseDto, UsageByUserDto, -} from 'src/domain/server-info/server-info.dto'; +} from 'src/dtos/server-info.dto'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { ImmichLogger } from 'src/infra/logger'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; diff --git a/server/src/domain/shared-link/shared-link.dto.ts b/server/src/domain/shared-link/shared-link.dto.ts deleted file mode 100644 index 73928aac2..000000000 --- a/server/src/domain/shared-link/shared-link.dto.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsString } from 'class-validator'; -import { SharedLinkType } from 'src/entities/shared-link.entity'; -import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; - -export class SharedLinkCreateDto { - @IsEnum(SharedLinkType) - @ApiProperty({ enum: SharedLinkType, enumName: 'SharedLinkType' }) - type!: SharedLinkType; - - @ValidateUUID({ each: true, optional: true }) - assetIds?: string[]; - - @ValidateUUID({ optional: true }) - albumId?: string; - - @IsString() - @Optional() - description?: string; - - @IsString() - @Optional() - password?: string; - - @ValidateDate({ optional: true, nullable: true }) - expiresAt?: Date | null = null; - - @ValidateBoolean({ optional: true }) - allowUpload?: boolean; - - @ValidateBoolean({ optional: true }) - allowDownload?: boolean = true; - - @ValidateBoolean({ optional: true }) - showMetadata?: boolean = true; -} - -export class SharedLinkEditDto { - @Optional() - description?: string; - - @Optional() - password?: string; - - @Optional({ nullable: true }) - expiresAt?: Date | null; - - @Optional() - allowUpload?: boolean; - - @ValidateBoolean({ optional: true }) - allowDownload?: boolean; - - @ValidateBoolean({ optional: true }) - showMetadata?: boolean; - - /** - * Few clients cannot send null to set the expiryTime to never. - * Setting this flag and not sending expiryAt is considered as null instead. - * Clients that can send null values can ignore this. - */ - @ValidateBoolean({ optional: true }) - changeExpiryTime?: boolean; -} - -export class SharedLinkPasswordDto { - @IsString() - @Optional() - @ApiProperty({ example: 'password' }) - password?: string; - - @IsString() - @Optional() - token?: string; -} diff --git a/server/src/domain/shared-link/shared-link.service.spec.ts b/server/src/domain/shared-link/shared-link.service.spec.ts index 926ec16b1..e5e3ea6e2 100644 --- a/server/src/domain/shared-link/shared-link.service.spec.ts +++ b/server/src/domain/shared-link/shared-link.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import _ from 'lodash'; -import { AssetIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto'; import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; +import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { SharedLinkType } from 'src/entities/shared-link.entity'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; diff --git a/server/src/domain/shared-link/shared-link.service.ts b/server/src/domain/shared-link/shared-link.service.ts index e01deee7b..a84a1f120 100644 --- a/server/src/domain/shared-link/shared-link.service.ts +++ b/server/src/domain/shared-link/shared-link.service.ts @@ -1,14 +1,16 @@ import { BadRequestException, ForbiddenException, Inject, Injectable, UnauthorizedException } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { + SharedLinkCreateDto, + SharedLinkEditDto, + SharedLinkPasswordDto, SharedLinkResponseDto, mapSharedLink, mapSharedLinkWithoutMetadata, -} from 'src/domain/shared-link/shared-link-response.dto'; -import { SharedLinkCreateDto, SharedLinkEditDto, SharedLinkPasswordDto } from 'src/domain/shared-link/shared-link.dto'; +} from 'src/dtos/shared-link.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { SharedLinkEntity, SharedLinkType } from 'src/entities/shared-link.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; diff --git a/server/src/domain/system-config/response-dto/system-config-template-storage-option.dto.ts b/server/src/domain/system-config/response-dto/system-config-template-storage-option.dto.ts deleted file mode 100644 index f0c8b9b64..000000000 --- a/server/src/domain/system-config/response-dto/system-config-template-storage-option.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -export class SystemConfigTemplateStorageOptionDto { - yearOptions!: string[]; - monthOptions!: string[]; - weekOptions!: string[]; - dayOptions!: string[]; - hourOptions!: string[]; - minuteOptions!: string[]; - secondOptions!: string[]; - presetOptions!: string[]; -} diff --git a/server/src/domain/system-config/system-config.service.ts b/server/src/domain/system-config/system-config.service.ts index d69593d21..c5837dca2 100644 --- a/server/src/domain/system-config/system-config.service.ts +++ b/server/src/domain/system-config/system-config.service.ts @@ -3,8 +3,6 @@ import { OnEvent } from '@nestjs/event-emitter'; import { instanceToPlain } from 'class-transformer'; import _ from 'lodash'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { SystemConfigDto, mapConfig } from 'src/domain/system-config/dto/system-config.dto'; -import { SystemConfigTemplateStorageOptionDto } from 'src/domain/system-config/response-dto/system-config-template-storage-option.dto'; import { supportedDayTokens, supportedHourTokens, @@ -15,6 +13,8 @@ import { supportedWeekTokens, supportedYearTokens, } from 'src/domain/system-config/system-config.constants'; +import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config-storage-template.dto'; +import { SystemConfigDto, mapConfig } from 'src/dtos/system-config.dto'; import { LogLevel, SystemConfig } from 'src/entities/system-config.entity'; import { ImmichLogger } from 'src/infra/logger'; import { diff --git a/server/src/domain/tag/tag.dto.ts b/server/src/domain/tag/tag.dto.ts deleted file mode 100644 index abf9549d8..000000000 --- a/server/src/domain/tag/tag.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { TagType } from 'src/entities/tag.entity'; -import { Optional } from 'src/validation'; - -export class CreateTagDto { - @IsString() - @IsNotEmpty() - name!: string; - - @IsEnum(TagType) - @IsNotEmpty() - @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) - type!: TagType; -} - -export class UpdateTagDto { - @IsString() - @Optional() - name?: string; -} diff --git a/server/src/domain/tag/tag.service.spec.ts b/server/src/domain/tag/tag.service.spec.ts index 9f630cdc0..c7aeba9f1 100644 --- a/server/src/domain/tag/tag.service.spec.ts +++ b/server/src/domain/tag/tag.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { when } from 'jest-when'; -import { AssetIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto'; import { TagService } from 'src/domain/tag/tag.service'; +import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { TagType } from 'src/entities/tag.entity'; import { ITagRepository } from 'src/interfaces/tag.repository'; import { assetStub } from 'test/fixtures/asset.stub'; diff --git a/server/src/domain/tag/tag.service.ts b/server/src/domain/tag/tag.service.ts index b04e251f7..b53eac0d7 100644 --- a/server/src/domain/tag/tag.service.ts +++ b/server/src/domain/tag/tag.service.ts @@ -1,10 +1,9 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import { AssetIdsDto } from 'src/domain/asset/dto/asset-ids.dto'; -import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { TagResponseDto, mapTag } from 'src/domain/tag/tag-response.dto'; -import { CreateTagDto, UpdateTagDto } from 'src/domain/tag/tag.dto'; +import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AssetIdsDto } from 'src/dtos/asset.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { CreateTagDto, TagResponseDto, UpdateTagDto, mapTag } from 'src/dtos/tag.dto'; import { ITagRepository } from 'src/interfaces/tag.repository'; @Injectable() diff --git a/server/src/domain/trash/trash.service.ts b/server/src/domain/trash/trash.service.ts index 2043b4248..9bece663b 100644 --- a/server/src/domain/trash/trash.service.ts +++ b/server/src/domain/trash/trash.service.ts @@ -1,9 +1,9 @@ import { Inject } from '@nestjs/common'; import { DateTime } from 'luxon'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; +import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; diff --git a/server/src/domain/user/dto/create-profile-image.dto.ts b/server/src/domain/user/dto/create-profile-image.dto.ts deleted file mode 100644 index 37a7d1340..000000000 --- a/server/src/domain/user/dto/create-profile-image.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { UploadFieldName } from 'src/domain/asset/asset.service'; - -export class CreateProfileImageDto { - @ApiProperty({ type: 'string', format: 'binary' }) - [UploadFieldName.PROFILE_DATA]!: Express.Multer.File; -} diff --git a/server/src/domain/user/dto/create-user.dto.ts b/server/src/domain/user/dto/create-user.dto.ts deleted file mode 100644 index 7861c58c2..000000000 --- a/server/src/domain/user/dto/create-user.dto.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsEmail, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator'; -import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation'; - -export class CreateUserDto { - @IsEmail({ require_tld: false }) - @Transform(toEmail) - email!: string; - - @IsNotEmpty() - @IsString() - password!: string; - - @IsNotEmpty() - @IsString() - name!: string; - - @Optional({ nullable: true }) - @IsString() - @Transform(toSanitized) - storageLabel?: string | null; - - @ValidateBoolean({ optional: true }) - memoriesEnabled?: boolean; - - @Optional({ nullable: true }) - @IsNumber() - @IsPositive() - @ApiProperty({ type: 'integer', format: 'int64' }) - quotaSizeInBytes?: number | null; - - @ValidateBoolean({ optional: true }) - shouldChangePassword?: boolean; -} - -export class CreateAdminDto { - @IsNotEmpty() - isAdmin!: true; - - @IsEmail({ require_tld: false }) - @Transform(({ value }) => value?.toLowerCase()) - email!: string; - - @IsNotEmpty() - password!: string; - - @IsNotEmpty() - name!: string; -} - -export class CreateUserOAuthDto { - @IsEmail({ require_tld: false }) - @Transform(({ value }) => value?.toLowerCase()) - email!: string; - - @IsNotEmpty() - oauthId!: string; - - name?: string; -} diff --git a/server/src/domain/user/dto/delete-user.dto.ts b/server/src/domain/user/dto/delete-user.dto.ts deleted file mode 100644 index aa41e18aa..000000000 --- a/server/src/domain/user/dto/delete-user.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ValidateBoolean } from 'src/validation'; - -export class DeleteUserDto { - @ValidateBoolean({ optional: true }) - force?: boolean; -} diff --git a/server/src/domain/user/dto/update-user.dto.spec.ts b/server/src/domain/user/dto/update-user.dto.spec.ts deleted file mode 100644 index 0ad407be3..000000000 --- a/server/src/domain/user/dto/update-user.dto.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { plainToInstance } from 'class-transformer'; -import { validate } from 'class-validator'; -import { UpdateUserDto } from 'src/domain/user/dto/update-user.dto'; - -describe('update user DTO', () => { - it('should allow emails without a tld', async () => { - const someEmail = 'test@test'; - - const dto = plainToInstance(UpdateUserDto, { - email: someEmail, - id: '3fe388e4-2078-44d7-b36c-39d9dee3a657', - }); - const errors = await validate(dto); - expect(errors).toHaveLength(0); - expect(dto.email).toEqual(someEmail); - }); -}); diff --git a/server/src/domain/user/dto/update-user.dto.ts b/server/src/domain/user/dto/update-user.dto.ts deleted file mode 100644 index 059971e6c..000000000 --- a/server/src/domain/user/dto/update-user.dto.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString, IsUUID } from 'class-validator'; -import { UserAvatarColor } from 'src/entities/user.entity'; -import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation'; - -export class UpdateUserDto { - @Optional() - @IsEmail({ require_tld: false }) - @Transform(toEmail) - email?: string; - - @Optional() - @IsNotEmpty() - @IsString() - password?: string; - - @Optional() - @IsString() - @IsNotEmpty() - name?: string; - - @Optional() - @IsString() - @Transform(toSanitized) - storageLabel?: string; - - @IsNotEmpty() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) - id!: string; - - @ValidateBoolean({ optional: true }) - isAdmin?: boolean; - - @ValidateBoolean({ optional: true }) - shouldChangePassword?: boolean; - - @ValidateBoolean({ optional: true }) - memoriesEnabled?: boolean; - - @Optional() - @IsEnum(UserAvatarColor) - @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) - avatarColor?: UserAvatarColor; - - @Optional({ nullable: true }) - @IsNumber() - @IsPositive() - @ApiProperty({ type: 'integer', format: 'int64' }) - quotaSizeInBytes?: number | null; -} diff --git a/server/src/domain/user/response-dto/create-profile-image-response.dto.ts b/server/src/domain/user/response-dto/create-profile-image-response.dto.ts deleted file mode 100644 index 2c7fd17be..000000000 --- a/server/src/domain/user/response-dto/create-profile-image-response.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class CreateProfileImageResponseDto { - userId!: string; - profileImagePath!: string; -} - -export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto { - return { - userId: userId, - profileImagePath: profileImagePath, - }; -} diff --git a/server/src/domain/user/response-dto/user-response.dto.ts b/server/src/domain/user/response-dto/user-response.dto.ts deleted file mode 100644 index 12ffca48c..000000000 --- a/server/src/domain/user/response-dto/user-response.dto.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum } from 'class-validator'; -import { UserAvatarColor, UserEntity, UserStatus } from 'src/entities/user.entity'; - -export const getRandomAvatarColor = (user: UserEntity): UserAvatarColor => { - const values = Object.values(UserAvatarColor); - const randomIndex = Math.floor( - [...user.email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, - ); - return values[randomIndex] as UserAvatarColor; -}; - -export class UserDto { - id!: string; - name!: string; - email!: string; - profileImagePath!: string; - @IsEnum(UserAvatarColor) - @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) - avatarColor!: UserAvatarColor; -} - -export class UserResponseDto extends UserDto { - storageLabel!: string | null; - shouldChangePassword!: boolean; - isAdmin!: boolean; - createdAt!: Date; - deletedAt!: Date | null; - updatedAt!: Date; - oauthId!: string; - memoriesEnabled?: boolean; - @ApiProperty({ type: 'integer', format: 'int64' }) - quotaSizeInBytes!: number | null; - @ApiProperty({ type: 'integer', format: 'int64' }) - quotaUsageInBytes!: number | null; - @ApiProperty({ enumName: 'UserStatus', enum: UserStatus }) - status!: string; -} - -export const mapSimpleUser = (entity: UserEntity): UserDto => { - return { - id: entity.id, - email: entity.email, - name: entity.name, - profileImagePath: entity.profileImagePath, - avatarColor: entity.avatarColor ?? getRandomAvatarColor(entity), - }; -}; - -export function mapUser(entity: UserEntity): UserResponseDto { - return { - ...mapSimpleUser(entity), - storageLabel: entity.storageLabel, - shouldChangePassword: entity.shouldChangePassword, - isAdmin: entity.isAdmin, - createdAt: entity.createdAt, - deletedAt: entity.deletedAt, - updatedAt: entity.updatedAt, - oauthId: entity.oauthId, - memoriesEnabled: entity.memoriesEnabled, - quotaSizeInBytes: entity.quotaSizeInBytes, - quotaUsageInBytes: entity.quotaUsageInBytes, - status: entity.status, - }; -} diff --git a/server/src/domain/user/user.service.spec.ts b/server/src/domain/user/user.service.spec.ts index 5b9a1de95..804a892c7 100644 --- a/server/src/domain/user/user.service.spec.ts +++ b/server/src/domain/user/user.service.spec.ts @@ -6,9 +6,8 @@ import { } from '@nestjs/common'; import { when } from 'jest-when'; import { JobName } from 'src/domain/job/job.constants'; -import { UpdateUserDto } from 'src/domain/user/dto/update-user.dto'; -import { mapUser } from 'src/domain/user/response-dto/user-response.dto'; import { UserService } from 'src/domain/user/user.service'; +import { UpdateUserDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; diff --git a/server/src/domain/user/user.service.ts b/server/src/domain/user/user.service.ts index 349a775ec..ef742123f 100644 --- a/server/src/domain/user/user.service.ts +++ b/server/src/domain/user/user.service.ts @@ -4,17 +4,11 @@ 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'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { JobName } from 'src/domain/job/job.constants'; import { IEntityJob } from 'src/domain/job/job.interface'; -import { CreateUserDto } from 'src/domain/user/dto/create-user.dto'; -import { DeleteUserDto } from 'src/domain/user/dto/delete-user.dto'; -import { UpdateUserDto } from 'src/domain/user/dto/update-user.dto'; -import { - CreateProfileImageResponseDto, - mapCreateProfileImageResponse, -} from 'src/domain/user/response-dto/create-profile-image-response.dto'; -import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { CreateProfileImageResponseDto, mapCreateProfileImageResponse } from 'src/dtos/user-profile.dto'; +import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; import { ImmichLogger } from 'src/infra/logger'; import { IAlbumRepository } from 'src/interfaces/album.repository'; diff --git a/server/src/domain/activity/activity.dto.ts b/server/src/dtos/activity.dto.ts similarity index 95% rename from server/src/domain/activity/activity.dto.ts rename to server/src/dtos/activity.dto.ts index a9d865778..bd0d40095 100644 --- a/server/src/domain/activity/activity.dto.ts +++ b/server/src/dtos/activity.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator'; -import { UserDto, mapSimpleUser } from 'src/domain/user/response-dto/user-response.dto'; +import { UserDto, mapSimpleUser } from 'src/dtos/user.dto'; import { ActivityEntity } from 'src/entities/activity.entity'; import { Optional, ValidateUUID } from 'src/validation'; diff --git a/server/src/domain/album/album-response.dto.spec.ts b/server/src/dtos/album-response.dto.spec.ts similarity index 89% rename from server/src/domain/album/album-response.dto.spec.ts rename to server/src/dtos/album-response.dto.spec.ts index 568b416b4..2a6d59abf 100644 --- a/server/src/domain/album/album-response.dto.spec.ts +++ b/server/src/dtos/album-response.dto.spec.ts @@ -1,4 +1,4 @@ -import { mapAlbum } from 'src/domain/album/album-response.dto'; +import { mapAlbum } from 'src/dtos/album.dto'; import { albumStub } from 'test/fixtures/album.stub'; describe('mapAlbum', () => { diff --git a/server/src/domain/album/album-response.dto.ts b/server/src/dtos/album.dto.ts similarity index 59% rename from server/src/domain/album/album-response.dto.ts rename to server/src/dtos/album.dto.ts index b016a1200..3f7af0f53 100644 --- a/server/src/domain/album/album-response.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -1,9 +1,87 @@ import { ApiProperty } from '@nestjs/swagger'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto'; +import { ArrayNotEmpty, IsEnum, IsString } from 'class-validator'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { AlbumEntity, AssetOrder } from 'src/entities/album.entity'; -import { Optional } from 'src/validation'; +import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; + +export class AlbumInfoDto { + @ValidateBoolean({ optional: true }) + withoutAssets?: boolean; +} + +export class AddUsersDto { + @ValidateUUID({ each: true }) + @ArrayNotEmpty() + sharedUserIds!: string[]; +} + +export class CreateAlbumDto { + @IsString() + @ApiProperty() + albumName!: string; + + @IsString() + @Optional() + description?: string; + + @ValidateUUID({ optional: true, each: true }) + sharedWithUserIds?: string[]; + + @ValidateUUID({ optional: true, each: true }) + assetIds?: string[]; +} + +export class UpdateAlbumDto { + @Optional() + @IsString() + albumName?: string; + + @Optional() + @IsString() + description?: string; + + @ValidateUUID({ optional: true }) + albumThumbnailAssetId?: string; + + @ValidateBoolean({ optional: true }) + isActivityEnabled?: boolean; + + @IsEnum(AssetOrder) + @Optional() + @ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' }) + order?: AssetOrder; +} + +export class GetAlbumsDto { + @ValidateBoolean({ optional: true }) + /** + * true: only shared albums + * false: only non-shared own albums + * undefined: shared and owned albums + */ + shared?: boolean; + + /** + * Only returns albums that contain the asset + * Ignores the shared parameter + * undefined: get all albums + */ + @ValidateUUID({ optional: true }) + assetId?: string; +} + +export class AlbumCountResponseDto { + @ApiProperty({ type: 'integer' }) + owned!: number; + + @ApiProperty({ type: 'integer' }) + shared!: number; + + @ApiProperty({ type: 'integer' }) + notShared!: number; +} export class AlbumResponseDto { id!: string; @@ -73,14 +151,3 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt export const mapAlbumWithAssets = (entity: AlbumEntity) => mapAlbum(entity, true); export const mapAlbumWithoutAssets = (entity: AlbumEntity) => mapAlbum(entity, false); - -export class AlbumCountResponseDto { - @ApiProperty({ type: 'integer' }) - owned!: number; - - @ApiProperty({ type: 'integer' }) - shared!: number; - - @ApiProperty({ type: 'integer' }) - notShared!: number; -} diff --git a/server/src/domain/api-key/api-key.dto.ts b/server/src/dtos/api-key.dto.ts similarity index 100% rename from server/src/domain/api-key/api-key.dto.ts rename to server/src/dtos/api-key.dto.ts diff --git a/server/src/domain/asset/response-dto/asset-ids-response.dto.ts b/server/src/dtos/asset-ids.response.dto.ts similarity index 100% rename from server/src/domain/asset/response-dto/asset-ids-response.dto.ts rename to server/src/dtos/asset-ids.response.dto.ts diff --git a/server/src/domain/asset/response-dto/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts similarity index 88% rename from server/src/domain/asset/response-dto/asset-response.dto.ts rename to server/src/dtos/asset-response.dto.ts index dff12d481..04e36645e 100644 --- a/server/src/domain/asset/response-dto/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -1,12 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { ExifResponseDto, mapExif } from 'src/domain/asset/response-dto/exif-response.dto'; -import { SmartInfoResponseDto, mapSmartInfo } from 'src/domain/asset/response-dto/smart-info-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; -import { PersonWithFacesResponseDto, mapFacesWithoutPerson, mapPerson } from 'src/domain/person/person.dto'; -import { TagResponseDto, mapTag } from 'src/domain/tag/tag-response.dto'; -import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { ExifResponseDto, mapExif } from 'src/dtos/exif.dto'; +import { PersonWithFacesResponseDto, mapFacesWithoutPerson, mapPerson } from 'src/dtos/person.dto'; +import { TagResponseDto, mapTag } from 'src/dtos/tag.dto'; +import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; +import { SmartInfoEntity } from 'src/entities/smart-info.entity'; export class SanitizedAssetResponseDto { id!: string; @@ -134,3 +134,15 @@ export class MemoryLaneResponseDto { title!: string; assets!: AssetResponseDto[]; } + +export class SmartInfoResponseDto { + tags?: string[] | null; + objects?: string[] | null; +} + +export function mapSmartInfo(entity: SmartInfoEntity): SmartInfoResponseDto { + return { + tags: entity.tags, + objects: entity.objects, + }; +} diff --git a/server/src/dtos/asset.dto.ts b/server/src/dtos/asset.dto.ts new file mode 100644 index 000000000..5ccf63c98 --- /dev/null +++ b/server/src/dtos/asset.dto.ts @@ -0,0 +1,132 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsDateString, + IsEnum, + IsInt, + IsLatitude, + IsLongitude, + IsNotEmpty, + IsPositive, + IsString, + ValidateIf, +} from 'class-validator'; +import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { AssetType } from 'src/entities/asset.entity'; +import { AssetStats } from 'src/interfaces/asset.repository'; +import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation'; + +export class DeviceIdDto { + @IsNotEmpty() + @IsString() + deviceId!: string; +} + +const hasGPS = (o: { latitude: undefined; longitude: undefined }) => + o.latitude !== undefined || o.longitude !== undefined; +const ValidateGPS = () => ValidateIf(hasGPS); + +export class UpdateAssetBase { + @ValidateBoolean({ optional: true }) + isFavorite?: boolean; + + @ValidateBoolean({ optional: true }) + isArchived?: boolean; + + @Optional() + @IsDateString() + dateTimeOriginal?: string; + + @ValidateGPS() + @IsLatitude() + @IsNotEmpty() + latitude?: number; + + @ValidateGPS() + @IsLongitude() + @IsNotEmpty() + longitude?: number; +} + +export class AssetBulkUpdateDto extends UpdateAssetBase { + @ValidateUUID({ each: true }) + ids!: string[]; + + @ValidateUUID({ optional: true }) + stackParentId?: string; + + @ValidateBoolean({ optional: true }) + removeParent?: boolean; +} + +export class UpdateAssetDto extends UpdateAssetBase { + @Optional() + @IsString() + description?: string; +} + +export class RandomAssetsDto { + @Optional() + @IsInt() + @IsPositive() + @Type(() => Number) + count?: number; +} + +export class AssetBulkDeleteDto extends BulkIdsDto { + @ValidateBoolean({ optional: true }) + force?: boolean; +} + +export class AssetIdsDto { + @ValidateUUID({ each: true }) + assetIds!: string[]; +} + +export enum AssetJobName { + REGENERATE_THUMBNAIL = 'regenerate-thumbnail', + REFRESH_METADATA = 'refresh-metadata', + TRANSCODE_VIDEO = 'transcode-video', +} + +export class AssetJobsDto extends AssetIdsDto { + @ApiProperty({ enumName: 'AssetJobName', enum: AssetJobName }) + @IsEnum(AssetJobName) + name!: AssetJobName; +} + +export class AssetStatsDto { + @ValidateBoolean({ optional: true }) + isArchived?: boolean; + + @ValidateBoolean({ optional: true }) + isFavorite?: boolean; + + @ValidateBoolean({ optional: true }) + isTrashed?: boolean; +} + +export class AssetStatsResponseDto { + @ApiProperty({ type: 'integer' }) + images!: number; + + @ApiProperty({ type: 'integer' }) + videos!: number; + + @ApiProperty({ type: 'integer' }) + total!: number; +} + +export const mapStats = (stats: AssetStats): AssetStatsResponseDto => { + return { + images: stats[AssetType.IMAGE], + videos: stats[AssetType.VIDEO], + total: Object.values(stats).reduce((total, value) => total + value, 0), + }; +}; +export enum UploadFieldName { + ASSET_DATA = 'assetData', + LIVE_PHOTO_DATA = 'livePhotoData', + SIDECAR_DATA = 'sidecarData', + PROFILE_DATA = 'file', +} diff --git a/server/src/domain/audit/audit.dto.ts b/server/src/dtos/audit.dto.ts similarity index 100% rename from server/src/domain/audit/audit.dto.ts rename to server/src/dtos/audit.dto.ts diff --git a/server/src/domain/auth/auth.dto.ts b/server/src/dtos/auth.dto.ts similarity index 100% rename from server/src/domain/auth/auth.dto.ts rename to server/src/dtos/auth.dto.ts diff --git a/server/src/domain/download/download.dto.ts b/server/src/dtos/download.dto.ts similarity index 100% rename from server/src/domain/download/download.dto.ts rename to server/src/dtos/download.dto.ts diff --git a/server/src/domain/asset/response-dto/exif-response.dto.ts b/server/src/dtos/exif.dto.ts similarity index 100% rename from server/src/domain/asset/response-dto/exif-response.dto.ts rename to server/src/dtos/exif.dto.ts diff --git a/server/src/domain/job/job.dto.ts b/server/src/dtos/job.dto.ts similarity index 100% rename from server/src/domain/job/job.dto.ts rename to server/src/dtos/job.dto.ts diff --git a/server/src/domain/library/library.dto.ts b/server/src/dtos/library.dto.ts similarity index 100% rename from server/src/domain/library/library.dto.ts rename to server/src/dtos/library.dto.ts diff --git a/server/src/domain/smart-info/dto/model-config.dto.ts b/server/src/dtos/model-config.dto.ts similarity index 100% rename from server/src/domain/smart-info/dto/model-config.dto.ts rename to server/src/dtos/model-config.dto.ts diff --git a/server/src/domain/partner/partner.dto.ts b/server/src/dtos/partner.dto.ts similarity index 71% rename from server/src/domain/partner/partner.dto.ts rename to server/src/dtos/partner.dto.ts index c197d2079..187f8f341 100644 --- a/server/src/domain/partner/partner.dto.ts +++ b/server/src/dtos/partner.dto.ts @@ -1,5 +1,5 @@ import { IsNotEmpty } from 'class-validator'; -import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto'; +import { UserResponseDto } from 'src/dtos/user.dto'; export class UpdatePartnerDto { @IsNotEmpty() diff --git a/server/src/domain/person/person.dto.ts b/server/src/dtos/person.dto.ts similarity index 98% rename from server/src/domain/person/person.dto.ts rename to server/src/dtos/person.dto.ts index 4153ba813..b28f18603 100644 --- a/server/src/domain/person/person.dto.ts +++ b/server/src/dtos/person.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsNotEmpty, IsString, MaxDate, ValidateNested } from 'class-validator'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { PersonEntity } from 'src/entities/person.entity'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; diff --git a/server/src/domain/search/dto/search.dto.ts b/server/src/dtos/search.dto.ts similarity index 66% rename from server/src/domain/search/dto/search.dto.ts rename to server/src/dtos/search.dto.ts index 4f77517b3..799baddee 100644 --- a/server/src/domain/search/dto/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -1,6 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator'; +import { AlbumResponseDto } from 'src/dtos/album.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AssetOrder } from 'src/entities/album.entity'; import { AssetType } from 'src/entities/asset.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; @@ -264,3 +266,130 @@ export function mapPlaces(place: GeodataPlacesEntity): PlacesResponseDto { admin2name: place.admin2Name, }; } +export enum SearchSuggestionType { + COUNTRY = 'country', + STATE = 'state', + CITY = 'city', + CAMERA_MAKE = 'camera-make', + CAMERA_MODEL = 'camera-model', +} + +export class SearchSuggestionRequestDto { + @IsEnum(SearchSuggestionType) + @IsNotEmpty() + @ApiProperty({ enumName: 'SearchSuggestionType', enum: SearchSuggestionType }) + type!: SearchSuggestionType; + + @IsString() + @Optional() + country?: string; + + @IsString() + @Optional() + state?: string; + + @IsString() + @Optional() + make?: string; + + @IsString() + @Optional() + model?: string; +} + +class SearchFacetCountResponseDto { + @ApiProperty({ type: 'integer' }) + count!: number; + value!: string; +} + +class SearchFacetResponseDto { + fieldName!: string; + counts!: SearchFacetCountResponseDto[]; +} + +class SearchAlbumResponseDto { + @ApiProperty({ type: 'integer' }) + total!: number; + @ApiProperty({ type: 'integer' }) + count!: number; + items!: AlbumResponseDto[]; + facets!: SearchFacetResponseDto[]; +} + +class SearchAssetResponseDto { + @ApiProperty({ type: 'integer' }) + total!: number; + @ApiProperty({ type: 'integer' }) + count!: number; + items!: AssetResponseDto[]; + facets!: SearchFacetResponseDto[]; + nextPage!: string | null; +} + +export class SearchResponseDto { + albums!: SearchAlbumResponseDto; + assets!: SearchAssetResponseDto; +} + +class SearchExploreItem { + value!: string; + data!: AssetResponseDto; +} + +export class SearchExploreResponseDto { + fieldName!: string; + items!: SearchExploreItem[]; +} + +export class MapMarkerDto { + @ValidateBoolean({ optional: true }) + isArchived?: boolean; + + @ValidateBoolean({ optional: true }) + isFavorite?: boolean; + + @ValidateDate({ optional: true }) + fileCreatedAfter?: Date; + + @ValidateDate({ optional: true }) + fileCreatedBefore?: Date; + + @ValidateBoolean({ optional: true }) + withPartners?: boolean; +} + +export class MemoryLaneDto { + @IsInt() + @Type(() => Number) + @Max(31) + @Min(1) + @ApiProperty({ type: 'integer' }) + day!: number; + + @IsInt() + @Type(() => Number) + @Max(12) + @Min(1) + @ApiProperty({ type: 'integer' }) + month!: number; +} +export class MapMarkerResponseDto { + @ApiProperty() + id!: string; + + @ApiProperty({ format: 'double' }) + lat!: number; + + @ApiProperty({ format: 'double' }) + lon!: number; + + @ApiProperty() + city!: string | null; + + @ApiProperty() + state!: string | null; + + @ApiProperty() + country!: string | null; +} diff --git a/server/src/domain/server-info/server-info.dto.ts b/server/src/dtos/server-info.dto.ts similarity index 96% rename from server/src/domain/server-info/server-info.dto.ts rename to server/src/dtos/server-info.dto.ts index 0cbfbe773..33fe83044 100644 --- a/server/src/domain/server-info/server-info.dto.ts +++ b/server/src/dtos/server-info.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger'; import type { DateTime } from 'luxon'; import { FeatureFlags } from 'src/cores/system-config.core'; import { IVersion, VersionType } from 'src/domain/domain.constant'; -import { SystemConfigThemeDto } from 'src/domain/system-config/dto/system-config-theme.dto'; +import { SystemConfigThemeDto } from 'src/dtos/system-config-theme.dto'; export class ServerPingResponse { @ApiResponseProperty({ type: String, example: 'pong' }) diff --git a/server/src/domain/shared-link/shared-link-response.dto.ts b/server/src/dtos/shared-link.dto.ts similarity index 57% rename from server/src/domain/shared-link/shared-link-response.dto.ts rename to server/src/dtos/shared-link.dto.ts index 44024506f..9a90901d2 100644 --- a/server/src/domain/shared-link/shared-link-response.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -1,9 +1,81 @@ import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsString } from 'class-validator'; import _ from 'lodash'; -import { AlbumResponseDto, mapAlbumWithoutAssets } from 'src/domain/album/album-response.dto'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; +import { AlbumResponseDto, mapAlbumWithoutAssets } from 'src/dtos/album.dto'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { SharedLinkEntity, SharedLinkType } from 'src/entities/shared-link.entity'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; +export class SharedLinkCreateDto { + @IsEnum(SharedLinkType) + @ApiProperty({ enum: SharedLinkType, enumName: 'SharedLinkType' }) + type!: SharedLinkType; + + @ValidateUUID({ each: true, optional: true }) + assetIds?: string[]; + + @ValidateUUID({ optional: true }) + albumId?: string; + + @IsString() + @Optional() + description?: string; + + @IsString() + @Optional() + password?: string; + + @ValidateDate({ optional: true, nullable: true }) + expiresAt?: Date | null = null; + + @ValidateBoolean({ optional: true }) + allowUpload?: boolean; + + @ValidateBoolean({ optional: true }) + allowDownload?: boolean = true; + + @ValidateBoolean({ optional: true }) + showMetadata?: boolean = true; +} + +export class SharedLinkEditDto { + @Optional() + description?: string; + + @Optional() + password?: string; + + @Optional({ nullable: true }) + expiresAt?: Date | null; + + @Optional() + allowUpload?: boolean; + + @ValidateBoolean({ optional: true }) + allowDownload?: boolean; + + @ValidateBoolean({ optional: true }) + showMetadata?: boolean; + + /** + * Few clients cannot send null to set the expiryTime to never. + * Setting this flag and not sending expiryAt is considered as null instead. + * Clients that can send null values can ignore this. + */ + @ValidateBoolean({ optional: true }) + changeExpiryTime?: boolean; +} + +export class SharedLinkPasswordDto { + @IsString() + @Optional() + @ApiProperty({ example: 'password' }) + password?: string; + + @IsString() + @Optional() + token?: string; +} export class SharedLinkResponseDto { id!: string; description!: string | null; diff --git a/server/src/domain/asset/dto/asset-stack.dto.ts b/server/src/dtos/stack.dto.ts similarity index 100% rename from server/src/domain/asset/dto/asset-stack.dto.ts rename to server/src/dtos/stack.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts b/server/src/dtos/system-config-ffmpeg.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts rename to server/src/dtos/system-config-ffmpeg.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-job.dto.ts b/server/src/dtos/system-config-job.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-job.dto.ts rename to server/src/dtos/system-config-job.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-library.dto.ts b/server/src/dtos/system-config-library.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-library.dto.ts rename to server/src/dtos/system-config-library.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-logging.dto.ts b/server/src/dtos/system-config-logging.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-logging.dto.ts rename to server/src/dtos/system-config-logging.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts b/server/src/dtos/system-config-machine-learning.dto.ts similarity index 85% rename from server/src/domain/system-config/dto/system-config-machine-learning.dto.ts rename to server/src/dtos/system-config-machine-learning.dto.ts index 058585992..7abc9ac88 100644 --- a/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts +++ b/server/src/dtos/system-config-machine-learning.dto.ts @@ -1,6 +1,6 @@ import { Type } from 'class-transformer'; import { IsObject, IsUrl, ValidateIf, ValidateNested } from 'class-validator'; -import { CLIPConfig, RecognitionConfig } from 'src/domain/smart-info/dto/model-config.dto'; +import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; import { ValidateBoolean } from 'src/validation'; export class SystemConfigMachineLearningDto { diff --git a/server/src/domain/system-config/dto/system-config-map.dto.ts b/server/src/dtos/system-config-map.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-map.dto.ts rename to server/src/dtos/system-config-map.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-new-version-check.dto.ts b/server/src/dtos/system-config-new-version-check.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-new-version-check.dto.ts rename to server/src/dtos/system-config-new-version-check.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-oauth.dto.ts b/server/src/dtos/system-config-oauth.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-oauth.dto.ts rename to server/src/dtos/system-config-oauth.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-password-login.dto.ts b/server/src/dtos/system-config-password-login.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-password-login.dto.ts rename to server/src/dtos/system-config-password-login.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts b/server/src/dtos/system-config-reverse-geocoding.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts rename to server/src/dtos/system-config-reverse-geocoding.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-server.dto.ts b/server/src/dtos/system-config-server.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-server.dto.ts rename to server/src/dtos/system-config-server.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-storage-template.dto.ts b/server/src/dtos/system-config-storage-template.dto.ts similarity index 53% rename from server/src/domain/system-config/dto/system-config-storage-template.dto.ts rename to server/src/dtos/system-config-storage-template.dto.ts index 77204b46e..d56cfedb8 100644 --- a/server/src/domain/system-config/dto/system-config-storage-template.dto.ts +++ b/server/src/dtos/system-config-storage-template.dto.ts @@ -12,3 +12,14 @@ export class SystemConfigStorageTemplateDto { @IsString() template!: string; } + +export class SystemConfigTemplateStorageOptionDto { + yearOptions!: string[]; + monthOptions!: string[]; + weekOptions!: string[]; + dayOptions!: string[]; + hourOptions!: string[]; + minuteOptions!: string[]; + secondOptions!: string[]; + presetOptions!: string[]; +} diff --git a/server/src/domain/system-config/dto/system-config-theme.dto.ts b/server/src/dtos/system-config-theme.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-theme.dto.ts rename to server/src/dtos/system-config-theme.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-thumbnail.dto.ts b/server/src/dtos/system-config-thumbnail.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-thumbnail.dto.ts rename to server/src/dtos/system-config-thumbnail.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-trash.dto.ts b/server/src/dtos/system-config-trash.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-trash.dto.ts rename to server/src/dtos/system-config-trash.dto.ts diff --git a/server/src/domain/system-config/dto/system-config-user.dto.ts b/server/src/dtos/system-config-user.dto.ts similarity index 100% rename from server/src/domain/system-config/dto/system-config-user.dto.ts rename to server/src/dtos/system-config-user.dto.ts diff --git a/server/src/domain/system-config/dto/system-config.dto.ts b/server/src/dtos/system-config.dto.ts similarity index 56% rename from server/src/domain/system-config/dto/system-config.dto.ts rename to server/src/dtos/system-config.dto.ts index 9aef92303..7c3237629 100644 --- a/server/src/domain/system-config/dto/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -1,21 +1,21 @@ import { Type } from 'class-transformer'; import { IsObject, ValidateNested } from 'class-validator'; -import { SystemConfigFFmpegDto } from 'src/domain/system-config/dto/system-config-ffmpeg.dto'; -import { SystemConfigJobDto } from 'src/domain/system-config/dto/system-config-job.dto'; -import { SystemConfigLibraryDto } from 'src/domain/system-config/dto/system-config-library.dto'; -import { SystemConfigLoggingDto } from 'src/domain/system-config/dto/system-config-logging.dto'; -import { SystemConfigMachineLearningDto } from 'src/domain/system-config/dto/system-config-machine-learning.dto'; -import { SystemConfigMapDto } from 'src/domain/system-config/dto/system-config-map.dto'; -import { SystemConfigNewVersionCheckDto } from 'src/domain/system-config/dto/system-config-new-version-check.dto'; -import { SystemConfigOAuthDto } from 'src/domain/system-config/dto/system-config-oauth.dto'; -import { SystemConfigPasswordLoginDto } from 'src/domain/system-config/dto/system-config-password-login.dto'; -import { SystemConfigReverseGeocodingDto } from 'src/domain/system-config/dto/system-config-reverse-geocoding.dto'; -import { SystemConfigServerDto } from 'src/domain/system-config/dto/system-config-server.dto'; -import { SystemConfigStorageTemplateDto } from 'src/domain/system-config/dto/system-config-storage-template.dto'; -import { SystemConfigThemeDto } from 'src/domain/system-config/dto/system-config-theme.dto'; -import { SystemConfigThumbnailDto } from 'src/domain/system-config/dto/system-config-thumbnail.dto'; -import { SystemConfigTrashDto } from 'src/domain/system-config/dto/system-config-trash.dto'; -import { SystemConfigUserDto } from 'src/domain/system-config/dto/system-config-user.dto'; +import { SystemConfigFFmpegDto } from 'src/dtos/system-config-ffmpeg.dto'; +import { SystemConfigJobDto } from 'src/dtos/system-config-job.dto'; +import { SystemConfigLibraryDto } from 'src/dtos/system-config-library.dto'; +import { SystemConfigLoggingDto } from 'src/dtos/system-config-logging.dto'; +import { SystemConfigMachineLearningDto } from 'src/dtos/system-config-machine-learning.dto'; +import { SystemConfigMapDto } from 'src/dtos/system-config-map.dto'; +import { SystemConfigNewVersionCheckDto } from 'src/dtos/system-config-new-version-check.dto'; +import { SystemConfigOAuthDto } from 'src/dtos/system-config-oauth.dto'; +import { SystemConfigPasswordLoginDto } from 'src/dtos/system-config-password-login.dto'; +import { SystemConfigReverseGeocodingDto } from 'src/dtos/system-config-reverse-geocoding.dto'; +import { SystemConfigServerDto } from 'src/dtos/system-config-server.dto'; +import { SystemConfigStorageTemplateDto } from 'src/dtos/system-config-storage-template.dto'; +import { SystemConfigThemeDto } from 'src/dtos/system-config-theme.dto'; +import { SystemConfigThumbnailDto } from 'src/dtos/system-config-thumbnail.dto'; +import { SystemConfigTrashDto } from 'src/dtos/system-config-trash.dto'; +import { SystemConfigUserDto } from 'src/dtos/system-config-user.dto'; import { SystemConfig } from 'src/entities/system-config.entity'; export class SystemConfigDto implements SystemConfig { diff --git a/server/src/domain/tag/tag-response.dto.ts b/server/src/dtos/tag.dto.ts similarity index 54% rename from server/src/domain/tag/tag-response.dto.ts rename to server/src/dtos/tag.dto.ts index 535efcf43..1094d70df 100644 --- a/server/src/domain/tag/tag-response.dto.ts +++ b/server/src/dtos/tag.dto.ts @@ -1,5 +1,24 @@ import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; import { TagEntity, TagType } from 'src/entities/tag.entity'; +import { Optional } from 'src/validation'; + +export class CreateTagDto { + @IsString() + @IsNotEmpty() + name!: string; + + @IsEnum(TagType) + @IsNotEmpty() + @ApiProperty({ enumName: 'TagTypeEnum', enum: TagType }) + type!: TagType; +} + +export class UpdateTagDto { + @IsString() + @Optional() + name?: string; +} export class TagResponseDto { id!: string; diff --git a/server/src/domain/asset/dto/time-bucket.dto.ts b/server/src/dtos/time-bucket.dto.ts similarity index 88% rename from server/src/domain/asset/dto/time-bucket.dto.ts rename to server/src/dtos/time-bucket.dto.ts index a5ff023d1..40dbb84e6 100644 --- a/server/src/domain/asset/dto/time-bucket.dto.ts +++ b/server/src/dtos/time-bucket.dto.ts @@ -44,3 +44,11 @@ export class TimeBucketAssetDto extends TimeBucketDto { @IsString() timeBucket!: string; } + +export class TimeBucketResponseDto { + @ApiProperty({ type: 'string' }) + timeBucket!: string; + + @ApiProperty({ type: 'integer' }) + count!: number; +} diff --git a/server/src/dtos/user-profile.dto.ts b/server/src/dtos/user-profile.dto.ts new file mode 100644 index 000000000..2f3d8cf22 --- /dev/null +++ b/server/src/dtos/user-profile.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { UploadFieldName } from 'src/dtos/asset.dto'; +import { UserAvatarColor, UserEntity } from 'src/entities/user.entity'; + +export class CreateProfileImageDto { + @ApiProperty({ type: 'string', format: 'binary' }) + [UploadFieldName.PROFILE_DATA]!: Express.Multer.File; +} + +export class CreateProfileImageResponseDto { + userId!: string; + profileImagePath!: string; +} + +export function mapCreateProfileImageResponse(userId: string, profileImagePath: string): CreateProfileImageResponseDto { + return { + userId: userId, + profileImagePath: profileImagePath, + }; +} + +export const getRandomAvatarColor = (user: UserEntity): UserAvatarColor => { + const values = Object.values(UserAvatarColor); + const randomIndex = Math.floor( + [...user.email].map((letter) => letter.codePointAt(0) ?? 0).reduce((a, b) => a + b, 0) % values.length, + ); + return values[randomIndex] as UserAvatarColor; +}; diff --git a/server/src/domain/user/dto/create-user.dto.spec.ts b/server/src/dtos/user.dto.spec.ts similarity index 79% rename from server/src/domain/user/dto/create-user.dto.spec.ts rename to server/src/dtos/user.dto.spec.ts index 28abc44ad..d07399f0e 100644 --- a/server/src/domain/user/dto/create-user.dto.spec.ts +++ b/server/src/dtos/user.dto.spec.ts @@ -1,6 +1,20 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; -import { CreateAdminDto, CreateUserDto, CreateUserOAuthDto } from 'src/domain/user/dto/create-user.dto'; +import { CreateAdminDto, CreateUserDto, CreateUserOAuthDto, UpdateUserDto } from 'src/dtos/user.dto'; + +describe('update user DTO', () => { + it('should allow emails without a tld', async () => { + const someEmail = 'test@test'; + + const dto = plainToInstance(UpdateUserDto, { + email: someEmail, + id: '3fe388e4-2078-44d7-b36c-39d9dee3a657', + }); + const errors = await validate(dto); + expect(errors).toHaveLength(0); + expect(dto.email).toEqual(someEmail); + }); +}); describe('create user DTO', () => { it('validates the email', async () => { diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts new file mode 100644 index 000000000..309006822 --- /dev/null +++ b/server/src/dtos/user.dto.ts @@ -0,0 +1,169 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString, IsUUID } from 'class-validator'; +import { getRandomAvatarColor } from 'src/dtos/user-profile.dto'; +import { UserAvatarColor, UserEntity, UserStatus } from 'src/entities/user.entity'; +import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation'; + +export class CreateUserDto { + @IsEmail({ require_tld: false }) + @Transform(toEmail) + email!: string; + + @IsNotEmpty() + @IsString() + password!: string; + + @IsNotEmpty() + @IsString() + name!: string; + + @Optional({ nullable: true }) + @IsString() + @Transform(toSanitized) + storageLabel?: string | null; + + @ValidateBoolean({ optional: true }) + memoriesEnabled?: boolean; + + @Optional({ nullable: true }) + @IsNumber() + @IsPositive() + @ApiProperty({ type: 'integer', format: 'int64' }) + quotaSizeInBytes?: number | null; + + @ValidateBoolean({ optional: true }) + shouldChangePassword?: boolean; +} + +export class CreateAdminDto { + @IsNotEmpty() + isAdmin!: true; + + @IsEmail({ require_tld: false }) + @Transform(({ value }) => value?.toLowerCase()) + email!: string; + + @IsNotEmpty() + password!: string; + + @IsNotEmpty() + name!: string; +} + +export class CreateUserOAuthDto { + @IsEmail({ require_tld: false }) + @Transform(({ value }) => value?.toLowerCase()) + email!: string; + + @IsNotEmpty() + oauthId!: string; + + name?: string; +} + +export class DeleteUserDto { + @ValidateBoolean({ optional: true }) + force?: boolean; +} + +export class UpdateUserDto { + @Optional() + @IsEmail({ require_tld: false }) + @Transform(toEmail) + email?: string; + + @Optional() + @IsNotEmpty() + @IsString() + password?: string; + + @Optional() + @IsString() + @IsNotEmpty() + name?: string; + + @Optional() + @IsString() + @Transform(toSanitized) + storageLabel?: string; + + @IsNotEmpty() + @IsUUID('4') + @ApiProperty({ format: 'uuid' }) + id!: string; + + @ValidateBoolean({ optional: true }) + isAdmin?: boolean; + + @ValidateBoolean({ optional: true }) + shouldChangePassword?: boolean; + + @ValidateBoolean({ optional: true }) + memoriesEnabled?: boolean; + + @Optional() + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor?: UserAvatarColor; + + @Optional({ nullable: true }) + @IsNumber() + @IsPositive() + @ApiProperty({ type: 'integer', format: 'int64' }) + quotaSizeInBytes?: number | null; +} + +export class UserDto { + id!: string; + name!: string; + email!: string; + profileImagePath!: string; + @IsEnum(UserAvatarColor) + @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor }) + avatarColor!: UserAvatarColor; +} + +export class UserResponseDto extends UserDto { + storageLabel!: string | null; + shouldChangePassword!: boolean; + isAdmin!: boolean; + createdAt!: Date; + deletedAt!: Date | null; + updatedAt!: Date; + oauthId!: string; + memoriesEnabled?: boolean; + @ApiProperty({ type: 'integer', format: 'int64' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer', format: 'int64' }) + quotaUsageInBytes!: number | null; + @ApiProperty({ enumName: 'UserStatus', enum: UserStatus }) + status!: string; +} + +export const mapSimpleUser = (entity: UserEntity): UserDto => { + return { + id: entity.id, + email: entity.email, + name: entity.name, + profileImagePath: entity.profileImagePath, + avatarColor: entity.avatarColor ?? getRandomAvatarColor(entity), + }; +}; + +export function mapUser(entity: UserEntity): UserResponseDto { + return { + ...mapSimpleUser(entity), + storageLabel: entity.storageLabel, + shouldChangePassword: entity.shouldChangePassword, + isAdmin: entity.isAdmin, + createdAt: entity.createdAt, + deletedAt: entity.deletedAt, + updatedAt: entity.updatedAt, + oauthId: entity.oauthId, + memoriesEnabled: entity.memoriesEnabled, + quotaSizeInBytes: entity.quotaSizeInBytes, + quotaUsageInBytes: entity.quotaUsageInBytes, + status: entity.status, + }; +} diff --git a/server/src/immich/api-v1/asset/asset.controller.ts b/server/src/immich/api-v1/asset/asset.controller.ts index d29f61fdd..5f57eb558 100644 --- a/server/src/immich/api-v1/asset/asset.controller.ts +++ b/server/src/immich/api-v1/asset/asset.controller.ts @@ -15,8 +15,8 @@ import { } from '@nestjs/common'; import { ApiBody, ApiConsumes, ApiHeader, ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { AssetService as AssetServiceV1 } from 'src/immich/api-v1/asset/asset.service'; import { AssetBulkUploadCheckDto } from 'src/immich/api-v1/asset/dto/asset-check.dto'; import { AssetSearchDto } from 'src/immich/api-v1/asset/dto/asset-search.dto'; diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index 0a5722690..ee310c377 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -7,10 +7,10 @@ import { } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; import { UploadFile } from 'src/domain/asset/asset.service'; -import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { mimeTypes } from 'src/domain/domain.constant'; import { JobName } from 'src/domain/job/job.constants'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; import { IAssetRepositoryV1 } from 'src/immich/api-v1/asset/asset-repository'; diff --git a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts index d16a9c05c..7e5b9a0c8 100644 --- a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts +++ b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; -import { UploadFieldName } from 'src/domain/asset/asset.service'; +import { UploadFieldName } from 'src/dtos/asset.dto'; import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation'; export class CreateAssetDto { diff --git a/server/src/interfaces/communication.repository.ts b/server/src/interfaces/communication.repository.ts index 870c0d937..4627e5265 100644 --- a/server/src/interfaces/communication.repository.ts +++ b/server/src/interfaces/communication.repository.ts @@ -1,5 +1,5 @@ -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { ReleaseNotification, ServerVersionResponseDto } from 'src/domain/server-info/server-info.dto'; +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'; diff --git a/server/src/interfaces/library.repository.ts b/server/src/interfaces/library.repository.ts index 5638d6024..dbc7fab81 100644 --- a/server/src/interfaces/library.repository.ts +++ b/server/src/interfaces/library.repository.ts @@ -1,4 +1,4 @@ -import { LibraryStatsResponseDto } from 'src/domain/library/library.dto'; +import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; export const ILibraryRepository = 'ILibraryRepository'; diff --git a/server/src/interfaces/machine-learning.repository.ts b/server/src/interfaces/machine-learning.repository.ts index d11e2e8f7..0aeed7635 100644 --- a/server/src/interfaces/machine-learning.repository.ts +++ b/server/src/interfaces/machine-learning.repository.ts @@ -1,4 +1,4 @@ -import { CLIPConfig, RecognitionConfig } from 'src/domain/smart-info/dto/model-config.dto'; +import { CLIPConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; export const IMachineLearningRepository = 'IMachineLearningRepository'; diff --git a/server/src/interfaces/storage.repository.ts b/server/src/interfaces/storage.repository.ts index 505e535b0..e78bb0195 100644 --- a/server/src/interfaces/storage.repository.ts +++ b/server/src/interfaces/storage.repository.ts @@ -2,7 +2,7 @@ import { WatchOptions } from 'chokidar'; import { Stats } from 'node:fs'; import { FileReadOptions } from 'node:fs/promises'; import { Readable } from 'node:stream'; -import { CrawlOptionsDto } from 'src/domain/library/library.dto'; +import { CrawlOptionsDto } from 'src/dtos/library.dto'; export interface ImmichReadStream { stream: Readable; diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 070bf15e8..765e24354 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -10,8 +10,8 @@ import { Reflector } from '@nestjs/core'; import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant'; -import { AuthDto } from 'src/domain/auth/auth.dto'; import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; +import { AuthDto } from 'src/dtos/auth.dto'; import { ImmichLogger } from 'src/infra/logger'; import { UAParser } from 'ua-parser-js'; diff --git a/server/src/middleware/file-upload.interceptor.ts b/server/src/middleware/file-upload.interceptor.ts index a7598f99d..9d6abaa3d 100644 --- a/server/src/middleware/file-upload.interceptor.ts +++ b/server/src/middleware/file-upload.interceptor.ts @@ -6,7 +6,8 @@ import { NextFunction, RequestHandler } from 'express'; import multer, { StorageEngine, diskStorage } from 'multer'; import { createHash, randomUUID } from 'node:crypto'; import { Observable } from 'rxjs'; -import { AssetService, UploadFieldName, UploadFile } from 'src/domain/asset/asset.service'; +import { AssetService, UploadFile } from 'src/domain/asset/asset.service'; +import { UploadFieldName } from 'src/dtos/asset.dto'; import { ImmichLogger } from 'src/infra/logger'; import { AuthRequest } from 'src/middleware/auth.guard'; diff --git a/server/src/repositories/filesystem.provider.spec.ts b/server/src/repositories/filesystem.provider.spec.ts index 56f54b036..c9790767c 100644 --- a/server/src/repositories/filesystem.provider.spec.ts +++ b/server/src/repositories/filesystem.provider.spec.ts @@ -1,5 +1,5 @@ import mockfs from 'mock-fs'; -import { CrawlOptionsDto } from 'src/domain/library/library.dto'; +import { CrawlOptionsDto } from 'src/dtos/library.dto'; import { FilesystemProvider } from 'src/repositories/filesystem.provider'; interface Test { diff --git a/server/src/repositories/filesystem.provider.ts b/server/src/repositories/filesystem.provider.ts index 2da49625e..1e65f9664 100644 --- a/server/src/repositories/filesystem.provider.ts +++ b/server/src/repositories/filesystem.provider.ts @@ -5,7 +5,7 @@ import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; import { mimeTypes } from 'src/domain/domain.constant'; -import { CrawlOptionsDto } from 'src/domain/library/library.dto'; +import { CrawlOptionsDto } from 'src/dtos/library.dto'; import { Instrumentation } from 'src/infra/instrumentation'; import { ImmichLogger } from 'src/infra/logger'; import { diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index 7171aad0d..152f544ce 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { LibraryStatsResponseDto } from 'src/domain/library/library.dto'; +import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; import { Instrumentation } from 'src/infra/instrumentation'; import { ILibraryRepository } from 'src/interfaces/library.repository'; diff --git a/server/src/repositories/machine-learning.repository.ts b/server/src/repositories/machine-learning.repository.ts index 46d16dcd7..34f0cdbfc 100644 --- a/server/src/repositories/machine-learning.repository.ts +++ b/server/src/repositories/machine-learning.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { readFile } from 'node:fs/promises'; -import { CLIPConfig, ModelConfig, RecognitionConfig } from 'src/domain/smart-info/dto/model-config.dto'; +import { CLIPConfig, ModelConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; import { Instrumentation } from 'src/infra/instrumentation'; import { CLIPMode, diff --git a/server/test/fixtures/auth.stub.ts b/server/test/fixtures/auth.stub.ts index e0add44d2..2e56d0001 100644 --- a/server/test/fixtures/auth.stub.ts +++ b/server/test/fixtures/auth.stub.ts @@ -1,4 +1,4 @@ -import { AuthDto } from 'src/domain/auth/auth.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { UserTokenEntity } from 'src/entities/user-token.entity'; import { UserEntity } from 'src/entities/user.entity'; diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 6855c8c7a..34e3da515 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -1,8 +1,8 @@ -import { AlbumResponseDto } from 'src/domain/album/album-response.dto'; -import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto'; -import { ExifResponseDto } from 'src/domain/asset/response-dto/exif-response.dto'; -import { SharedLinkResponseDto } from 'src/domain/shared-link/shared-link-response.dto'; -import { mapUser } from 'src/domain/user/response-dto/user-response.dto'; +import { AlbumResponseDto } from 'src/dtos/album.dto'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { ExifResponseDto } from 'src/dtos/exif.dto'; +import { SharedLinkResponseDto } from 'src/dtos/shared-link.dto'; +import { mapUser } from 'src/dtos/user.dto'; import { AssetOrder } from 'src/entities/album.entity'; import { AssetType } from 'src/entities/asset.entity'; import { SharedLinkEntity, SharedLinkType } from 'src/entities/shared-link.entity'; diff --git a/server/test/fixtures/tag.stub.ts b/server/test/fixtures/tag.stub.ts index 27123ddb6..537c65db4 100644 --- a/server/test/fixtures/tag.stub.ts +++ b/server/test/fixtures/tag.stub.ts @@ -1,4 +1,4 @@ -import { TagResponseDto } from 'src/domain/tag/tag-response.dto'; +import { TagResponseDto } from 'src/dtos/tag.dto'; import { TagEntity, TagType } from 'src/entities/tag.entity'; import { userStub } from 'test/fixtures/user.stub'; From ef4a492cb14424de3a791004af6604a795e1eae9 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu, 21 Mar 2024 00:07:30 +0100 Subject: [PATCH 03/33] chore(server): move services (#8133) move services --- .vscode/settings.json | 4 ++ .../jobs/specs/library-watcher.e2e-spec.ts | 2 +- server/src/apps/api.service.ts | 14 +++--- server/src/apps/app.module.ts | 48 +++++++++---------- server/src/apps/microservices.service.ts | 26 +++++----- server/src/commands/list-users.command.ts | 2 +- server/src/commands/oauth-login.ts | 2 +- server/src/commands/password-login.ts | 2 +- .../commands/reset-admin-password.command.ts | 2 +- server/src/controllers/activity.controller.ts | 2 +- server/src/controllers/album.controller.ts | 2 +- server/src/controllers/api-key.controller.ts | 2 +- server/src/controllers/app.controller.ts | 2 +- server/src/controllers/asset.controller.ts | 4 +- server/src/controllers/audit.controller.ts | 2 +- server/src/controllers/auth.controller.ts | 2 +- server/src/controllers/download.controller.ts | 2 +- server/src/controllers/face.controller.ts | 2 +- server/src/controllers/job.controller.ts | 2 +- server/src/controllers/library.controller.ts | 2 +- server/src/controllers/oauth.controller.ts | 2 +- server/src/controllers/partner.controller.ts | 2 +- server/src/controllers/person.controller.ts | 2 +- server/src/controllers/search.controller.ts | 2 +- .../src/controllers/server-info.controller.ts | 2 +- .../src/controllers/shared-link.controller.ts | 2 +- .../controllers/system-config.controller.ts | 4 +- server/src/controllers/tag.controller.ts | 2 +- server/src/controllers/trash.controller.ts | 2 +- server/src/controllers/user.controller.ts | 2 +- .../system-config-map-theme.dto.ts | 0 .../src/immich/api-v1/asset/asset.service.ts | 2 +- server/src/middleware/auth.guard.ts | 2 +- .../src/middleware/file-upload.interceptor.ts | 2 +- .../repositories/communication.repository.ts | 2 +- .../activity.service.spec.ts} | 2 +- .../activity => services}/activity.service.ts | 0 .../album => services}/album.service.spec.ts | 2 +- .../album => services}/album.service.ts | 0 .../api-key.service.spec.ts | 2 +- .../api-key => services}/api-key.service.ts | 0 .../asset => services}/asset.service.spec.ts | 2 +- .../asset => services}/asset.service.ts | 0 .../audit => services}/audit.service.spec.ts | 2 +- .../audit => services}/audit.service.ts | 0 .../auth => services}/auth.service.spec.ts | 2 +- .../{domain/auth => services}/auth.service.ts | 0 .../database.service.spec.ts | 2 +- .../database => services}/database.service.ts | 0 .../download.service.spec.ts | 2 +- .../download => services}/download.service.ts | 0 .../job => services}/job.service.spec.ts | 2 +- .../{domain/job => services}/job.service.ts | 0 .../library.service.spec.ts | 2 +- .../library => services}/library.service.ts | 0 .../media => services}/media.service.spec.ts | 2 +- .../media => services}/media.service.ts | 0 .../metadata.service.spec.ts | 2 +- .../metadata => services}/metadata.service.ts | 0 .../partner.service.spec.ts | 2 +- .../partner => services}/partner.service.ts | 0 .../person.service.spec.ts | 2 +- .../person => services}/person.service.ts | 0 .../search.service.spec.ts | 2 +- .../search => services}/search.service.ts | 0 .../server-info.service.spec.ts | 2 +- .../server-info.service.ts | 0 .../shared-link.service.spec.ts | 2 +- .../shared-link.service.ts | 0 .../smart-info.service.spec.ts | 2 +- .../smart-info.service.ts | 0 .../storage-template.service.spec.ts | 2 +- .../storage-template.service.ts | 0 .../storage.service.spec.ts | 2 +- .../storage => services}/storage.service.ts | 0 .../system-config.service.spec.ts | 2 +- .../system-config.service.ts | 0 .../tag => services}/tag.service.spec.ts | 2 +- .../{domain/tag => services}/tag.service.ts | 0 .../trash => services}/trash.service.spec.ts | 2 +- .../trash => services}/trash.service.ts | 0 .../user => services}/user.service.spec.ts | 2 +- .../{domain/user => services}/user.service.ts | 0 83 files changed, 104 insertions(+), 100 deletions(-) rename server/src/{domain/system-config => dtos}/system-config-map-theme.dto.ts (100%) rename server/src/{domain/activity/activity.spec.ts => services/activity.service.spec.ts} (98%) rename server/src/{domain/activity => services}/activity.service.ts (100%) rename server/src/{domain/album => services}/album.service.spec.ts (99%) rename server/src/{domain/album => services}/album.service.ts (100%) rename server/src/{domain/api-key => services}/api-key.service.spec.ts (98%) rename server/src/{domain/api-key => services}/api-key.service.ts (100%) rename server/src/{domain/asset => services}/asset.service.spec.ts (99%) rename server/src/{domain/asset => services}/asset.service.ts (100%) rename server/src/{domain/audit => services}/audit.service.spec.ts (98%) rename server/src/{domain/audit => services}/audit.service.ts (100%) rename server/src/{domain/auth => services}/auth.service.spec.ts (99%) rename server/src/{domain/auth => services}/auth.service.ts (100%) rename server/src/{domain/database => services}/database.service.spec.ts (99%) rename server/src/{domain/database => services}/database.service.ts (100%) rename server/src/{domain/download => services}/download.service.spec.ts (99%) rename server/src/{domain/download => services}/download.service.ts (100%) rename server/src/{domain/job => services}/job.service.spec.ts (99%) rename server/src/{domain/job => services}/job.service.ts (100%) rename server/src/{domain/library => services}/library.service.spec.ts (99%) rename server/src/{domain/library => services}/library.service.ts (100%) rename server/src/{domain/media => services}/media.service.spec.ts (99%) rename server/src/{domain/media => services}/media.service.ts (100%) rename server/src/{domain/metadata => services}/metadata.service.spec.ts (99%) rename server/src/{domain/metadata => services}/metadata.service.ts (100%) rename server/src/{domain/partner => services}/partner.service.spec.ts (98%) rename server/src/{domain/partner => services}/partner.service.ts (100%) rename server/src/{domain/person => services}/person.service.spec.ts (99%) rename server/src/{domain/person => services}/person.service.ts (100%) rename server/src/{domain/search => services}/search.service.spec.ts (99%) rename server/src/{domain/search => services}/search.service.ts (100%) rename server/src/{domain/server-info => services}/server-info.service.spec.ts (99%) rename server/src/{domain/server-info => services}/server-info.service.ts (100%) rename server/src/{domain/shared-link => services}/shared-link.service.spec.ts (99%) rename server/src/{domain/shared-link => services}/shared-link.service.ts (100%) rename server/src/{domain/smart-info => services}/smart-info.service.spec.ts (98%) rename server/src/{domain/smart-info => services}/smart-info.service.ts (100%) rename server/src/{domain/storage-template => services}/storage-template.service.spec.ts (99%) rename server/src/{domain/storage-template => services}/storage-template.service.ts (100%) rename server/src/{domain/storage => services}/storage.service.spec.ts (95%) rename server/src/{domain/storage => services}/storage.service.ts (100%) rename server/src/{domain/system-config => services}/system-config.service.spec.ts (99%) rename server/src/{domain/system-config => services}/system-config.service.ts (100%) rename server/src/{domain/tag => services}/tag.service.spec.ts (99%) rename server/src/{domain/tag => services}/tag.service.ts (100%) rename server/src/{domain/trash => services}/trash.service.spec.ts (98%) rename server/src/{domain/trash => services}/trash.service.ts (100%) rename server/src/{domain/user => services}/user.service.spec.ts (99%) rename server/src/{domain/user => services}/user.service.ts (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json index eae6e1a16..3267c6713 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,4 +27,8 @@ "cSpell.words": [ "immich" ], + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.patterns": { + "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + } } \ No newline at end of file diff --git a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts index 0a005c7d5..5ce0868b9 100644 --- a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts +++ b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts @@ -1,12 +1,12 @@ import { api } from 'e2e/client'; import fs from 'node:fs/promises'; import path from 'node:path'; -import { LibraryService } from 'src/domain/library/library.service'; import { LoginResponseDto } from 'src/dtos/auth.dto'; import { LibraryResponseDto } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; import { StorageEventType } from 'src/interfaces/storage.repository'; +import { LibraryService } from 'src/services/library.service'; import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, diff --git a/server/src/apps/api.service.ts b/server/src/apps/api.service.ts index 31134cefa..fbd0d0e4a 100644 --- a/server/src/apps/api.service.ts +++ b/server/src/apps/api.service.ts @@ -3,15 +3,15 @@ import { Cron, CronExpression, Interval } from '@nestjs/schedule'; import { NextFunction, Request, Response } from 'express'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; -import { AuthService } from 'src/domain/auth/auth.service'; -import { DatabaseService } from 'src/domain/database/database.service'; import { ONE_HOUR, WEB_ROOT } from 'src/domain/domain.constant'; -import { JobService } from 'src/domain/job/job.service'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; -import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; -import { StorageService } from 'src/domain/storage/storage.service'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; import { ImmichLogger } from 'src/infra/logger'; +import { AuthService } from 'src/services/auth.service'; +import { DatabaseService } from 'src/services/database.service'; +import { JobService } from 'src/services/job.service'; +import { ServerInfoService } from 'src/services/server-info.service'; +import { SharedLinkService } from 'src/services/shared-link.service'; +import { StorageService } from 'src/services/storage.service'; +import { SystemConfigService } from 'src/services/system-config.service'; import { OpenGraphTags } from 'src/utils'; const render = (index: string, meta: OpenGraphTags) => { diff --git a/server/src/apps/app.module.ts b/server/src/apps/app.module.ts index f134d3363..964e2d4e1 100644 --- a/server/src/apps/app.module.ts +++ b/server/src/apps/app.module.ts @@ -6,30 +6,6 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; import { OpenTelemetryModule } from 'nestjs-otel'; import { bullConfig, bullQueues, immichAppConfig } from 'src/config'; -import { ActivityService } from 'src/domain/activity/activity.service'; -import { AlbumService } from 'src/domain/album/album.service'; -import { APIKeyService } from 'src/domain/api-key/api-key.service'; -import { AssetService } from 'src/domain/asset/asset.service'; -import { AuditService } from 'src/domain/audit/audit.service'; -import { AuthService } from 'src/domain/auth/auth.service'; -import { DatabaseService } from 'src/domain/database/database.service'; -import { DownloadService } from 'src/domain/download/download.service'; -import { JobService } from 'src/domain/job/job.service'; -import { LibraryService } from 'src/domain/library/library.service'; -import { MediaService } from 'src/domain/media/media.service'; -import { MetadataService } from 'src/domain/metadata/metadata.service'; -import { PartnerService } from 'src/domain/partner/partner.service'; -import { PersonService } from 'src/domain/person/person.service'; -import { SearchService } from 'src/domain/search/search.service'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; -import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; -import { SmartInfoService } from 'src/domain/smart-info/smart-info.service'; -import { StorageTemplateService } from 'src/domain/storage-template/storage-template.service'; -import { StorageService } from 'src/domain/storage/storage.service'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; -import { TagService } from 'src/domain/tag/tag.service'; -import { TrashService } from 'src/domain/trash/trash.service'; -import { UserService } from 'src/domain/user/user.service'; import { databaseEntities } from 'src/entities'; import { databaseConfig } from 'src/infra/database.config'; import { otelConfig } from 'src/infra/instrumentation'; @@ -88,6 +64,30 @@ import { SystemMetadataRepository } from 'src/repositories/system-metadata.repos import { TagRepository } from 'src/repositories/tag.repository'; import { UserTokenRepository } from 'src/repositories/user-token.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { ActivityService } from 'src/services/activity.service'; +import { AlbumService } from 'src/services/album.service'; +import { APIKeyService } from 'src/services/api-key.service'; +import { AssetService } from 'src/services/asset.service'; +import { AuditService } from 'src/services/audit.service'; +import { AuthService } from 'src/services/auth.service'; +import { DatabaseService } from 'src/services/database.service'; +import { DownloadService } from 'src/services/download.service'; +import { JobService } from 'src/services/job.service'; +import { LibraryService } from 'src/services/library.service'; +import { MediaService } from 'src/services/media.service'; +import { MetadataService } from 'src/services/metadata.service'; +import { PartnerService } from 'src/services/partner.service'; +import { PersonService } from 'src/services/person.service'; +import { SearchService } from 'src/services/search.service'; +import { ServerInfoService } from 'src/services/server-info.service'; +import { SharedLinkService } from 'src/services/shared-link.service'; +import { SmartInfoService } from 'src/services/smart-info.service'; +import { StorageTemplateService } from 'src/services/storage-template.service'; +import { StorageService } from 'src/services/storage.service'; +import { SystemConfigService } from 'src/services/system-config.service'; +import { TagService } from 'src/services/tag.service'; +import { TrashService } from 'src/services/trash.service'; +import { UserService } from 'src/services/user.service'; const services: Provider[] = [ APIKeyService, diff --git a/server/src/apps/microservices.service.ts b/server/src/apps/microservices.service.ts index bfdc37576..8f11583be 100644 --- a/server/src/apps/microservices.service.ts +++ b/server/src/apps/microservices.service.ts @@ -1,20 +1,20 @@ import { Injectable } from '@nestjs/common'; -import { AssetService } from 'src/domain/asset/asset.service'; -import { AuditService } from 'src/domain/audit/audit.service'; -import { DatabaseService } from 'src/domain/database/database.service'; import { JobName } from 'src/domain/job/job.constants'; import { IDeleteFilesJob } from 'src/domain/job/job.interface'; -import { JobService } from 'src/domain/job/job.service'; -import { LibraryService } from 'src/domain/library/library.service'; -import { MediaService } from 'src/domain/media/media.service'; -import { MetadataService } from 'src/domain/metadata/metadata.service'; -import { PersonService } from 'src/domain/person/person.service'; -import { SmartInfoService } from 'src/domain/smart-info/smart-info.service'; -import { StorageTemplateService } from 'src/domain/storage-template/storage-template.service'; -import { StorageService } from 'src/domain/storage/storage.service'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; -import { UserService } from 'src/domain/user/user.service'; import { otelSDK } from 'src/infra/instrumentation'; +import { AssetService } from 'src/services/asset.service'; +import { AuditService } from 'src/services/audit.service'; +import { DatabaseService } from 'src/services/database.service'; +import { JobService } from 'src/services/job.service'; +import { LibraryService } from 'src/services/library.service'; +import { MediaService } from 'src/services/media.service'; +import { MetadataService } from 'src/services/metadata.service'; +import { PersonService } from 'src/services/person.service'; +import { SmartInfoService } from 'src/services/smart-info.service'; +import { StorageTemplateService } from 'src/services/storage-template.service'; +import { StorageService } from 'src/services/storage.service'; +import { SystemConfigService } from 'src/services/system-config.service'; +import { UserService } from 'src/services/user.service'; @Injectable() export class MicroservicesService { diff --git a/server/src/commands/list-users.command.ts b/server/src/commands/list-users.command.ts index 222833fad..32bcc35d9 100644 --- a/server/src/commands/list-users.command.ts +++ b/server/src/commands/list-users.command.ts @@ -1,6 +1,6 @@ import { Command, CommandRunner } from 'nest-commander'; -import { UserService } from 'src/domain/user/user.service'; import { UserEntity } from 'src/entities/user.entity'; +import { UserService } from 'src/services/user.service'; @Command({ name: 'list-users', diff --git a/server/src/commands/oauth-login.ts b/server/src/commands/oauth-login.ts index 12562fae1..c9bb4d5ef 100644 --- a/server/src/commands/oauth-login.ts +++ b/server/src/commands/oauth-login.ts @@ -1,5 +1,5 @@ import { Command, CommandRunner } from 'nest-commander'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; +import { SystemConfigService } from 'src/services/system-config.service'; @Command({ name: 'enable-oauth-login', diff --git a/server/src/commands/password-login.ts b/server/src/commands/password-login.ts index 953a1293a..3d992f858 100644 --- a/server/src/commands/password-login.ts +++ b/server/src/commands/password-login.ts @@ -1,5 +1,5 @@ import { Command, CommandRunner } from 'nest-commander'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; +import { SystemConfigService } from 'src/services/system-config.service'; @Command({ name: 'enable-password-login', diff --git a/server/src/commands/reset-admin-password.command.ts b/server/src/commands/reset-admin-password.command.ts index b67ef418e..a186603a3 100644 --- a/server/src/commands/reset-admin-password.command.ts +++ b/server/src/commands/reset-admin-password.command.ts @@ -1,6 +1,6 @@ import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander'; -import { UserService } from 'src/domain/user/user.service'; import { UserResponseDto } from 'src/dtos/user.dto'; +import { UserService } from 'src/services/user.service'; @Command({ name: 'reset-admin-password', diff --git a/server/src/controllers/activity.controller.ts b/server/src/controllers/activity.controller.ts index 92e8797aa..a65b284ca 100644 --- a/server/src/controllers/activity.controller.ts +++ b/server/src/controllers/activity.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Response } from 'express'; -import { ActivityService } from 'src/domain/activity/activity.service'; import { ActivityCreateDto, ActivityDto, @@ -11,6 +10,7 @@ import { } from 'src/dtos/activity.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { ActivityService } from 'src/services/activity.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Activity') diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index 7cc39a860..c4b11fbb4 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -1,6 +1,5 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AlbumService } from 'src/domain/album/album.service'; import { AddUsersDto, AlbumCountResponseDto, @@ -13,6 +12,7 @@ import { import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; +import { AlbumService } from 'src/services/album.service'; import { ParseMeUUIDPipe, UUIDParamDto } from 'src/validation'; @ApiTags('Album') diff --git a/server/src/controllers/api-key.controller.ts b/server/src/controllers/api-key.controller.ts index 9b6838f8d..564b90387 100644 --- a/server/src/controllers/api-key.controller.ts +++ b/server/src/controllers/api-key.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { APIKeyService } from 'src/domain/api-key/api-key.service'; import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { APIKeyService } from 'src/services/api-key.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('API Key') diff --git a/server/src/controllers/app.controller.ts b/server/src/controllers/app.controller.ts index d4c7ea5b4..472d0da3f 100644 --- a/server/src/controllers/app.controller.ts +++ b/server/src/controllers/app.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Header } from '@nestjs/common'; import { ApiExcludeEndpoint } from '@nestjs/swagger'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; import { PublicRoute } from 'src/middleware/auth.guard'; +import { SystemConfigService } from 'src/services/system-config.service'; @Controller() export class AppController { diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index ca44aedfe..37e169113 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -1,7 +1,5 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { AssetService } from 'src/domain/asset/asset.service'; -import { SearchService } from 'src/domain/search/search.service'; import { AssetResponseDto, MemoryLaneResponseDto } from 'src/dtos/asset-response.dto'; import { AssetBulkDeleteDto, @@ -19,6 +17,8 @@ import { UpdateStackParentDto } from 'src/dtos/stack.dto'; import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; import { Route } from 'src/middleware/file-upload.interceptor'; +import { AssetService } from 'src/services/asset.service'; +import { SearchService } from 'src/services/search.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Asset') diff --git a/server/src/controllers/audit.controller.ts b/server/src/controllers/audit.controller.ts index b4d27b4ce..1487e78d4 100644 --- a/server/src/controllers/audit.controller.ts +++ b/server/src/controllers/audit.controller.ts @@ -1,6 +1,5 @@ import { Body, Controller, Get, Post, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AuditService } from 'src/domain/audit/audit.service'; import { AuditDeletesDto, AuditDeletesResponseDto, @@ -11,6 +10,7 @@ import { } from 'src/dtos/audit.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { AdminRoute, Auth, Authenticated } from 'src/middleware/auth.guard'; +import { AuditService } from 'src/services/audit.service'; @ApiTags('Audit') @Controller('audit') diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 6197c1e5f..cbb649dc0 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -2,7 +2,6 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Req, import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/domain/auth/auth.constant'; -import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; import { AuthDeviceResponseDto, AuthDto, @@ -15,6 +14,7 @@ import { } from 'src/dtos/auth.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; +import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Authentication') diff --git a/server/src/controllers/download.controller.ts b/server/src/controllers/download.controller.ts index 8ba5d4281..66cdc1fac 100644 --- a/server/src/controllers/download.controller.ts +++ b/server/src/controllers/download.controller.ts @@ -1,12 +1,12 @@ import { Body, Controller, HttpCode, HttpStatus, Next, Param, Post, Res, StreamableFile } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { DownloadService } from 'src/domain/download/download.service'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; import { asStreamableFile, sendFile } from 'src/immich/app.utils'; import { Auth, Authenticated, FileResponse, SharedLinkRoute } from 'src/middleware/auth.guard'; +import { DownloadService } from 'src/services/download.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Download') diff --git a/server/src/controllers/face.controller.ts b/server/src/controllers/face.controller.ts index d5712d6b7..a3f33fb86 100644 --- a/server/src/controllers/face.controller.ts +++ b/server/src/controllers/face.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Get, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { PersonService } from 'src/domain/person/person.service'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetFaceResponseDto, FaceDto, PersonResponseDto } from 'src/dtos/person.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { PersonService } from 'src/services/person.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Face') diff --git a/server/src/controllers/job.controller.ts b/server/src/controllers/job.controller.ts index 04dea79ee..d6bd45b1e 100644 --- a/server/src/controllers/job.controller.ts +++ b/server/src/controllers/job.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Get, Param, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { JobService } from 'src/domain/job/job.service'; import { AllJobStatusResponseDto, JobCommandDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto'; import { Authenticated } from 'src/middleware/auth.guard'; +import { JobService } from 'src/services/job.service'; @ApiTags('Job') @Controller('jobs') diff --git a/server/src/controllers/library.controller.ts b/server/src/controllers/library.controller.ts index 96c5f7415..70d357187 100644 --- a/server/src/controllers/library.controller.ts +++ b/server/src/controllers/library.controller.ts @@ -1,6 +1,5 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { LibraryService } from 'src/domain/library/library.service'; import { CreateLibraryDto, LibraryResponseDto, @@ -12,6 +11,7 @@ import { ValidateLibraryResponseDto, } from 'src/dtos/library.dto'; import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; +import { LibraryService } from 'src/services/library.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Library') diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index 6da9faa17..debbd4e67 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, HttpStatus, Post, Redirect, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; -import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; import { AuthDto, LoginResponseDto, @@ -11,6 +10,7 @@ import { } from 'src/dtos/auth.dto'; import { UserResponseDto } from 'src/dtos/user.dto'; import { Auth, Authenticated, GetLoginDetails, PublicRoute } from 'src/middleware/auth.guard'; +import { AuthService, LoginDetails } from 'src/services/auth.service'; @ApiTags('OAuth') @Controller('oauth') diff --git a/server/src/controllers/partner.controller.ts b/server/src/controllers/partner.controller.ts index c4f22a04f..c7d5613f7 100644 --- a/server/src/controllers/partner.controller.ts +++ b/server/src/controllers/partner.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; import { ApiQuery, ApiTags } from '@nestjs/swagger'; -import { PartnerService } from 'src/domain/partner/partner.service'; import { AuthDto } from 'src/dtos/auth.dto'; import { PartnerResponseDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; import { PartnerDirection } from 'src/interfaces/partner.repository'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { PartnerService } from 'src/services/partner.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Partner') diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index 9c5602091..5e45fc929 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, Next, Param, Post, Put, Query, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { PersonService } from 'src/domain/person/person.service'; import { BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -18,6 +17,7 @@ import { } from 'src/dtos/person.dto'; import { sendFile } from 'src/immich/app.utils'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; +import { PersonService } from 'src/services/person.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Person') diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 6b88f5ad8..eaf45be29 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -1,6 +1,5 @@ import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { SearchService } from 'src/domain/search/search.service'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PersonResponseDto } from 'src/dtos/person.dto'; @@ -16,6 +15,7 @@ import { SmartSearchDto, } from 'src/dtos/search.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { SearchService } from 'src/services/search.service'; @ApiTags('Search') @Controller('search') diff --git a/server/src/controllers/server-info.controller.ts b/server/src/controllers/server-info.controller.ts index 9937acde8..e32b0d191 100644 --- a/server/src/controllers/server-info.controller.ts +++ b/server/src/controllers/server-info.controller.ts @@ -1,6 +1,5 @@ import { Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; import { ServerConfigDto, ServerFeaturesDto, @@ -12,6 +11,7 @@ import { ServerVersionResponseDto, } from 'src/dtos/server-info.dto'; import { AdminRoute, Authenticated, PublicRoute } from 'src/middleware/auth.guard'; +import { ServerInfoService } from 'src/services/server-info.service'; @ApiTags('Server Info') @Controller('server-info') diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 90bbd6ee1..61dd6605a 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -2,7 +2,6 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/domain/auth/auth.constant'; -import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -13,6 +12,7 @@ import { SharedLinkResponseDto, } from 'src/dtos/shared-link.dto'; import { Auth, Authenticated, SharedLinkRoute } from 'src/middleware/auth.guard'; +import { SharedLinkService } from 'src/services/shared-link.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Shared Link') diff --git a/server/src/controllers/system-config.controller.ts b/server/src/controllers/system-config.controller.ts index 34f9f2e0e..ca2bbb5f5 100644 --- a/server/src/controllers/system-config.controller.ts +++ b/server/src/controllers/system-config.controller.ts @@ -1,10 +1,10 @@ import { Body, Controller, Get, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { MapThemeDto } from 'src/domain/system-config/system-config-map-theme.dto'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; +import { MapThemeDto } from 'src/dtos/system-config-map-theme.dto'; import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config-storage-template.dto'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AdminRoute, Authenticated } from 'src/middleware/auth.guard'; +import { SystemConfigService } from 'src/services/system-config.service'; @ApiTags('System Config') @Controller('system-config') diff --git a/server/src/controllers/tag.controller.ts b/server/src/controllers/tag.controller.ts index bbca4a05d..1caed8d52 100644 --- a/server/src/controllers/tag.controller.ts +++ b/server/src/controllers/tag.controller.ts @@ -1,12 +1,12 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { TagService } from 'src/domain/tag/tag.service'; import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { CreateTagDto, TagResponseDto, UpdateTagDto } from 'src/dtos/tag.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { TagService } from 'src/services/tag.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('Tag') diff --git a/server/src/controllers/trash.controller.ts b/server/src/controllers/trash.controller.ts index 5ea692d48..25df3543c 100644 --- a/server/src/controllers/trash.controller.ts +++ b/server/src/controllers/trash.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { TrashService } from 'src/domain/trash/trash.service'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { TrashService } from 'src/services/trash.service'; @ApiTags('Trash') @Controller('trash') diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts index 9fc0ff320..0663ae197 100644 --- a/server/src/controllers/user.controller.ts +++ b/server/src/controllers/user.controller.ts @@ -16,13 +16,13 @@ import { } from '@nestjs/common'; import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; import { NextFunction, Response } from 'express'; -import { UserService } from 'src/domain/user/user.service'; import { AuthDto } from 'src/dtos/auth.dto'; import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto } from 'src/dtos/user.dto'; import { sendFile } from 'src/immich/app.utils'; import { AdminRoute, Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor'; +import { UserService } from 'src/services/user.service'; import { UUIDParamDto } from 'src/validation'; @ApiTags('User') diff --git a/server/src/domain/system-config/system-config-map-theme.dto.ts b/server/src/dtos/system-config-map-theme.dto.ts similarity index 100% rename from server/src/domain/system-config/system-config-map-theme.dto.ts rename to server/src/dtos/system-config-map-theme.dto.ts diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index ee310c377..023017130 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -6,7 +6,6 @@ import { NotFoundException, } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { UploadFile } from 'src/domain/asset/asset.service'; import { mimeTypes } from 'src/domain/domain.constant'; import { JobName } from 'src/domain/job/job.constants'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; @@ -36,6 +35,7 @@ import { IJobRepository } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { UploadFile } from 'src/services/asset.service'; import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils'; import { QueryFailedError } from 'typeorm'; diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 765e24354..48befd9a2 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -10,9 +10,9 @@ import { Reflector } from '@nestjs/core'; import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant'; -import { AuthService, LoginDetails } from 'src/domain/auth/auth.service'; import { AuthDto } from 'src/dtos/auth.dto'; import { ImmichLogger } from 'src/infra/logger'; +import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UAParser } from 'ua-parser-js'; export enum Metadata { diff --git a/server/src/middleware/file-upload.interceptor.ts b/server/src/middleware/file-upload.interceptor.ts index 9d6abaa3d..f1d5c5c34 100644 --- a/server/src/middleware/file-upload.interceptor.ts +++ b/server/src/middleware/file-upload.interceptor.ts @@ -6,10 +6,10 @@ import { NextFunction, RequestHandler } from 'express'; import multer, { StorageEngine, diskStorage } from 'multer'; import { createHash, randomUUID } from 'node:crypto'; import { Observable } from 'rxjs'; -import { AssetService, UploadFile } from 'src/domain/asset/asset.service'; import { UploadFieldName } from 'src/dtos/asset.dto'; import { ImmichLogger } from 'src/infra/logger'; import { AuthRequest } from 'src/middleware/auth.guard'; +import { AssetService, UploadFile } from 'src/services/asset.service'; export enum Route { ASSET = 'asset', diff --git a/server/src/repositories/communication.repository.ts b/server/src/repositories/communication.repository.ts index e92fe1387..549975c74 100644 --- a/server/src/repositories/communication.repository.ts +++ b/server/src/repositories/communication.repository.ts @@ -7,7 +7,6 @@ import { WebSocketServer, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; -import { AuthService } from 'src/domain/auth/auth.service'; import { Instrumentation } from 'src/infra/instrumentation'; import { ImmichLogger } from 'src/infra/logger'; import { @@ -18,6 +17,7 @@ import { OnServerEventCallback, ServerEvent, } from 'src/interfaces/communication.repository'; +import { AuthService } from 'src/services/auth.service'; @Instrumentation() @WebSocketGateway({ diff --git a/server/src/domain/activity/activity.spec.ts b/server/src/services/activity.service.spec.ts similarity index 98% rename from server/src/domain/activity/activity.spec.ts rename to server/src/services/activity.service.spec.ts index 6013f0ff0..a36d82554 100644 --- a/server/src/domain/activity/activity.spec.ts +++ b/server/src/services/activity.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; -import { ActivityService } from 'src/domain/activity/activity.service'; import { ReactionType } from 'src/dtos/activity.dto'; import { IActivityRepository } from 'src/interfaces/activity.repository'; +import { ActivityService } from 'src/services/activity.service'; import { activityStub } from 'test/fixtures/activity.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; diff --git a/server/src/domain/activity/activity.service.ts b/server/src/services/activity.service.ts similarity index 100% rename from server/src/domain/activity/activity.service.ts rename to server/src/services/activity.service.ts diff --git a/server/src/domain/album/album.service.spec.ts b/server/src/services/album.service.spec.ts similarity index 99% rename from server/src/domain/album/album.service.spec.ts rename to server/src/services/album.service.spec.ts index 41b505ea6..6a09f54d1 100644 --- a/server/src/domain/album/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -1,10 +1,10 @@ import { BadRequestException } from '@nestjs/common'; import _ from 'lodash'; -import { AlbumService } from 'src/domain/album/album.service'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { AlbumService } from 'src/services/album.service'; import { albumStub } from 'test/fixtures/album.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/src/domain/album/album.service.ts b/server/src/services/album.service.ts similarity index 100% rename from server/src/domain/album/album.service.ts rename to server/src/services/album.service.ts diff --git a/server/src/domain/api-key/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts similarity index 98% rename from server/src/domain/api-key/api-key.service.spec.ts rename to server/src/services/api-key.service.spec.ts index 0400c13a2..66dd70d0f 100644 --- a/server/src/domain/api-key/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; -import { APIKeyService } from 'src/domain/api-key/api-key.service'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; +import { APIKeyService } from 'src/services/api-key.service'; import { keyStub } from 'test/fixtures/api-key.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock'; diff --git a/server/src/domain/api-key/api-key.service.ts b/server/src/services/api-key.service.ts similarity index 100% rename from server/src/domain/api-key/api-key.service.ts rename to server/src/services/api-key.service.ts diff --git a/server/src/domain/asset/asset.service.spec.ts b/server/src/services/asset.service.spec.ts similarity index 99% rename from server/src/domain/asset/asset.service.spec.ts rename to server/src/services/asset.service.spec.ts index a3603c3da..3a6385c03 100644 --- a/server/src/domain/asset/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -1,6 +1,5 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; import { when } from 'jest-when'; -import { AssetService } from 'src/domain/asset/asset.service'; import { JobName } from 'src/domain/job/job.constants'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/asset.dto'; @@ -13,6 +12,7 @@ import { IPartnerRepository } from 'src/interfaces/partner.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { AssetService } from 'src/services/asset.service'; import { assetStackStub, assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { faceStub } from 'test/fixtures/face.stub'; diff --git a/server/src/domain/asset/asset.service.ts b/server/src/services/asset.service.ts similarity index 100% rename from server/src/domain/asset/asset.service.ts rename to server/src/services/asset.service.ts diff --git a/server/src/domain/audit/audit.service.spec.ts b/server/src/services/audit.service.spec.ts similarity index 98% rename from server/src/domain/audit/audit.service.spec.ts rename to server/src/services/audit.service.spec.ts index ecda57b08..14d08f916 100644 --- a/server/src/domain/audit/audit.service.spec.ts +++ b/server/src/services/audit.service.spec.ts @@ -1,4 +1,3 @@ -import { AuditService } from 'src/domain/audit/audit.service'; import { DatabaseAction, EntityType } from 'src/entities/audit.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IAuditRepository } from 'src/interfaces/audit.repository'; @@ -7,6 +6,7 @@ import { JobStatus } from 'src/interfaces/job.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { AuditService } from 'src/services/audit.service'; import { auditStub } from 'test/fixtures/audit.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; diff --git a/server/src/domain/audit/audit.service.ts b/server/src/services/audit.service.ts similarity index 100% rename from server/src/domain/audit/audit.service.ts rename to server/src/services/audit.service.ts diff --git a/server/src/domain/auth/auth.service.spec.ts b/server/src/services/auth.service.spec.ts similarity index 99% rename from server/src/domain/auth/auth.service.spec.ts rename to server/src/services/auth.service.spec.ts index 37c759937..dc7565e4d 100644 --- a/server/src/domain/auth/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -3,7 +3,6 @@ import { IncomingHttpHeaders } from 'node:http'; import { Issuer, generators } from 'openid-client'; import { Socket } from 'socket.io'; import { AuthType } from 'src/domain/auth/auth.constant'; -import { AuthService } from 'src/domain/auth/auth.service'; import { AuthDto, SignUpDto } from 'src/dtos/auth.dto'; import { UserEntity } from 'src/entities/user.entity'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; @@ -13,6 +12,7 @@ import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserTokenRepository } from 'src/interfaces/user-token.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { AuthService } from 'src/services/auth.service'; import { keyStub } from 'test/fixtures/api-key.stub'; import { authStub, loginResponseStub } from 'test/fixtures/auth.stub'; import { sharedLinkStub } from 'test/fixtures/shared-link.stub'; diff --git a/server/src/domain/auth/auth.service.ts b/server/src/services/auth.service.ts similarity index 100% rename from server/src/domain/auth/auth.service.ts rename to server/src/services/auth.service.ts diff --git a/server/src/domain/database/database.service.spec.ts b/server/src/services/database.service.spec.ts similarity index 99% rename from server/src/domain/database/database.service.spec.ts rename to server/src/services/database.service.spec.ts index 191c0221b..dac857a07 100644 --- a/server/src/domain/database/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -1,7 +1,7 @@ -import { DatabaseService } from 'src/domain/database/database.service'; import { Version, VersionType } from 'src/domain/domain.constant'; import { ImmichLogger } from 'src/infra/logger'; import { DatabaseExtension, IDatabaseRepository, VectorIndex } from 'src/interfaces/database.repository'; +import { DatabaseService } from 'src/services/database.service'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; describe(DatabaseService.name, () => { diff --git a/server/src/domain/database/database.service.ts b/server/src/services/database.service.ts similarity index 100% rename from server/src/domain/database/database.service.ts rename to server/src/services/database.service.ts diff --git a/server/src/domain/download/download.service.spec.ts b/server/src/services/download.service.spec.ts similarity index 99% rename from server/src/domain/download/download.service.spec.ts rename to server/src/services/download.service.spec.ts index cfdff82b9..ab8389955 100644 --- a/server/src/domain/download/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,9 +1,9 @@ import { BadRequestException } from '@nestjs/common'; import { when } from 'jest-when'; -import { DownloadService } from 'src/domain/download/download.service'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; +import { DownloadService } from 'src/services/download.service'; import { CacheControl, ImmichFileResponse } from 'src/utils'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/domain/download/download.service.ts b/server/src/services/download.service.ts similarity index 100% rename from server/src/domain/download/download.service.ts rename to server/src/services/download.service.ts diff --git a/server/src/domain/job/job.service.spec.ts b/server/src/services/job.service.spec.ts similarity index 99% rename from server/src/domain/job/job.service.spec.ts rename to server/src/services/job.service.spec.ts index ec905b062..4be9786a2 100644 --- a/server/src/domain/job/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -1,13 +1,13 @@ import { BadRequestException } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; import { JobCommand, JobName, QueueName } from 'src/domain/job/job.constants'; -import { JobService } from 'src/domain/job/job.service'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ICommunicationRepository } from 'src/interfaces/communication.repository'; import { IJobRepository, JobHandler, JobItem, JobStatus } from 'src/interfaces/job.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +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'; diff --git a/server/src/domain/job/job.service.ts b/server/src/services/job.service.ts similarity index 100% rename from server/src/domain/job/job.service.ts rename to server/src/services/job.service.ts diff --git a/server/src/domain/library/library.service.spec.ts b/server/src/services/library.service.spec.ts similarity index 99% rename from server/src/domain/library/library.service.spec.ts rename to server/src/services/library.service.spec.ts index 81bf1cc86..2382d83c2 100644 --- a/server/src/domain/library/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -5,7 +5,6 @@ import { Stats } from 'node:fs'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { JobName } from 'src/domain/job/job.constants'; import { ILibraryFileJob, ILibraryRefreshJob } from 'src/domain/job/job.interface'; -import { LibraryService } from 'src/domain/library/library.service'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; @@ -18,6 +17,7 @@ import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { LibraryService } from 'src/services/library.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { libraryStub } from 'test/fixtures/library.stub'; diff --git a/server/src/domain/library/library.service.ts b/server/src/services/library.service.ts similarity index 100% rename from server/src/domain/library/library.service.ts rename to server/src/services/library.service.ts diff --git a/server/src/domain/media/media.service.spec.ts b/server/src/services/media.service.spec.ts similarity index 99% rename from server/src/domain/media/media.service.spec.ts rename to server/src/services/media.service.spec.ts index 97a6d161b..eb9407e12 100644 --- a/server/src/domain/media/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,6 +1,5 @@ import { Stats } from 'node:fs'; import { JobName } from 'src/domain/job/job.constants'; -import { MediaService } from 'src/domain/media/media.service'; import { AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { @@ -20,6 +19,7 @@ import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { MediaService } from 'src/services/media.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { faceStub } from 'test/fixtures/face.stub'; import { probeStub } from 'test/fixtures/media.stub'; diff --git a/server/src/domain/media/media.service.ts b/server/src/services/media.service.ts similarity index 100% rename from server/src/domain/media/media.service.ts rename to server/src/services/media.service.ts diff --git a/server/src/domain/metadata/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts similarity index 99% rename from server/src/domain/metadata/metadata.service.spec.ts rename to server/src/services/metadata.service.spec.ts index 2108c5fc4..a235aca04 100644 --- a/server/src/domain/metadata/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -4,7 +4,6 @@ import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; import { JobName } from 'src/domain/job/job.constants'; -import { MetadataService, Orientation } from 'src/domain/metadata/metadata.service'; import { AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { SystemConfigKey } from 'src/entities/system-config.entity'; @@ -20,6 +19,7 @@ import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { MetadataService, Orientation } from 'src/services/metadata.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { fileStub } from 'test/fixtures/file.stub'; import { probeStub } from 'test/fixtures/media.stub'; diff --git a/server/src/domain/metadata/metadata.service.ts b/server/src/services/metadata.service.ts similarity index 100% rename from server/src/domain/metadata/metadata.service.ts rename to server/src/services/metadata.service.ts diff --git a/server/src/domain/partner/partner.service.spec.ts b/server/src/services/partner.service.spec.ts similarity index 98% rename from server/src/domain/partner/partner.service.spec.ts rename to server/src/services/partner.service.spec.ts index 1256a9ca0..94ec3b099 100644 --- a/server/src/domain/partner/partner.service.spec.ts +++ b/server/src/services/partner.service.spec.ts @@ -1,9 +1,9 @@ import { BadRequestException } from '@nestjs/common'; -import { PartnerService } from 'src/domain/partner/partner.service'; import { PartnerResponseDto } from 'src/dtos/partner.dto'; import { UserAvatarColor } from 'src/entities/user.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IPartnerRepository, PartnerDirection } from 'src/interfaces/partner.repository'; +import { PartnerService } from 'src/services/partner.service'; import { authStub } from 'test/fixtures/auth.stub'; import { partnerStub } from 'test/fixtures/partner.stub'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; diff --git a/server/src/domain/partner/partner.service.ts b/server/src/services/partner.service.ts similarity index 100% rename from server/src/domain/partner/partner.service.ts rename to server/src/services/partner.service.ts diff --git a/server/src/domain/person/person.service.spec.ts b/server/src/services/person.service.spec.ts similarity index 99% rename from server/src/domain/person/person.service.spec.ts rename to server/src/services/person.service.spec.ts index 3f555e34f..7abc9f3d9 100644 --- a/server/src/domain/person/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -1,6 +1,5 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; import { JobName } from 'src/domain/job/job.constants'; -import { PersonService } from 'src/domain/person/person.service'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; @@ -15,6 +14,7 @@ import { IPersonRepository } from 'src/interfaces/person.repository'; import { FaceSearchResult, ISearchRepository } from 'src/interfaces/search.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { PersonService } from 'src/services/person.service'; import { CacheControl, ImmichFileResponse } from 'src/utils'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/domain/person/person.service.ts b/server/src/services/person.service.ts similarity index 100% rename from server/src/domain/person/person.service.ts rename to server/src/services/person.service.ts diff --git a/server/src/domain/search/search.service.spec.ts b/server/src/services/search.service.spec.ts similarity index 99% rename from server/src/domain/search/search.service.spec.ts rename to server/src/services/search.service.spec.ts index 7f83f2ff0..c7566ea94 100644 --- a/server/src/domain/search/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -1,4 +1,3 @@ -import { SearchService } from 'src/domain/search/search.service'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { SearchDto } from 'src/dtos/search.dto'; import { SystemConfigKey } from 'src/entities/system-config.entity'; @@ -9,6 +8,7 @@ import { IPartnerRepository } from 'src/interfaces/partner.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { SearchService } from 'src/services/search.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { personStub } from 'test/fixtures/person.stub'; diff --git a/server/src/domain/search/search.service.ts b/server/src/services/search.service.ts similarity index 100% rename from server/src/domain/search/search.service.ts rename to server/src/services/search.service.ts diff --git a/server/src/domain/server-info/server-info.service.spec.ts b/server/src/services/server-info.service.spec.ts similarity index 99% rename from server/src/domain/server-info/server-info.service.spec.ts rename to server/src/services/server-info.service.spec.ts index 1685f8bda..0db7597b8 100644 --- a/server/src/domain/server-info/server-info.service.spec.ts +++ b/server/src/services/server-info.service.spec.ts @@ -1,5 +1,4 @@ import { serverVersion } from 'src/domain/domain.constant'; -import { ServerInfoService } from 'src/domain/server-info/server-info.service'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { ICommunicationRepository } from 'src/interfaces/communication.repository'; import { IServerInfoRepository } from 'src/interfaces/server-info.repository'; @@ -7,6 +6,7 @@ import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { ServerInfoService } from 'src/services/server-info.service'; import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; diff --git a/server/src/domain/server-info/server-info.service.ts b/server/src/services/server-info.service.ts similarity index 100% rename from server/src/domain/server-info/server-info.service.ts rename to server/src/services/server-info.service.ts diff --git a/server/src/domain/shared-link/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts similarity index 99% rename from server/src/domain/shared-link/shared-link.service.spec.ts rename to server/src/services/shared-link.service.spec.ts index e5e3ea6e2..611d9b670 100644 --- a/server/src/domain/shared-link/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -1,10 +1,10 @@ import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import _ from 'lodash'; -import { SharedLinkService } from 'src/domain/shared-link/shared-link.service'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { SharedLinkType } from 'src/entities/shared-link.entity'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; +import { SharedLinkService } from 'src/services/shared-link.service'; import { albumStub } from 'test/fixtures/album.stub'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/domain/shared-link/shared-link.service.ts b/server/src/services/shared-link.service.ts similarity index 100% rename from server/src/domain/shared-link/shared-link.service.ts rename to server/src/services/shared-link.service.ts diff --git a/server/src/domain/smart-info/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts similarity index 98% rename from server/src/domain/smart-info/smart-info.service.spec.ts rename to server/src/services/smart-info.service.spec.ts index 29ce75fb4..a1b8943f2 100644 --- a/server/src/domain/smart-info/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,6 +1,5 @@ import { JobName } from 'src/domain/job/job.constants'; import { cleanModelName, getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant'; -import { SmartInfoService } from 'src/domain/smart-info/smart-info.service'; import { AssetEntity } from 'src/entities/asset.entity'; import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; @@ -9,6 +8,7 @@ import { IJobRepository } from 'src/interfaces/job.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { SmartInfoService } from 'src/services/smart-info.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; diff --git a/server/src/domain/smart-info/smart-info.service.ts b/server/src/services/smart-info.service.ts similarity index 100% rename from server/src/domain/smart-info/smart-info.service.ts rename to server/src/services/smart-info.service.ts diff --git a/server/src/domain/storage-template/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts similarity index 99% rename from server/src/domain/storage-template/storage-template.service.spec.ts rename to server/src/services/storage-template.service.spec.ts index ecadb3e86..b11d1f534 100644 --- a/server/src/domain/storage-template/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,7 +1,6 @@ import { when } from 'jest-when'; import { Stats } from 'node:fs'; import { SystemConfigCore, defaults } from 'src/cores/system-config.core'; -import { StorageTemplateService } from 'src/domain/storage-template/storage-template.service'; import { AssetPathType } from 'src/entities/move.entity'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.repository'; @@ -14,6 +13,7 @@ import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { StorageTemplateService } from 'src/services/storage-template.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { userStub } from 'test/fixtures/user.stub'; import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; diff --git a/server/src/domain/storage-template/storage-template.service.ts b/server/src/services/storage-template.service.ts similarity index 100% rename from server/src/domain/storage-template/storage-template.service.ts rename to server/src/services/storage-template.service.ts diff --git a/server/src/domain/storage/storage.service.spec.ts b/server/src/services/storage.service.spec.ts similarity index 95% rename from server/src/domain/storage/storage.service.spec.ts rename to server/src/services/storage.service.spec.ts index 2791c9630..4a5607efe 100644 --- a/server/src/domain/storage/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -1,5 +1,5 @@ -import { StorageService } from 'src/domain/storage/storage.service'; import { IStorageRepository } from 'src/interfaces/storage.repository'; +import { StorageService } from 'src/services/storage.service'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; describe(StorageService.name, () => { diff --git a/server/src/domain/storage/storage.service.ts b/server/src/services/storage.service.ts similarity index 100% rename from server/src/domain/storage/storage.service.ts rename to server/src/services/storage.service.ts diff --git a/server/src/domain/system-config/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts similarity index 99% rename from server/src/domain/system-config/system-config.service.spec.ts rename to server/src/services/system-config.service.spec.ts index f81d726eb..b5b5875d7 100644 --- a/server/src/domain/system-config/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -1,7 +1,6 @@ import { BadRequestException } from '@nestjs/common'; import { defaults } from 'src/cores/system-config.core'; import { QueueName } from 'src/domain/job/job.constants'; -import { SystemConfigService } from 'src/domain/system-config/system-config.service'; import { AudioCodec, CQMode, @@ -19,6 +18,7 @@ import { ImmichLogger } from 'src/infra/logger'; import { ICommunicationRepository, ServerEvent } from 'src/interfaces/communication.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { SystemConfigService } from 'src/services/system-config.service'; import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; diff --git a/server/src/domain/system-config/system-config.service.ts b/server/src/services/system-config.service.ts similarity index 100% rename from server/src/domain/system-config/system-config.service.ts rename to server/src/services/system-config.service.ts diff --git a/server/src/domain/tag/tag.service.spec.ts b/server/src/services/tag.service.spec.ts similarity index 99% rename from server/src/domain/tag/tag.service.spec.ts rename to server/src/services/tag.service.spec.ts index c7aeba9f1..0dca425c3 100644 --- a/server/src/domain/tag/tag.service.spec.ts +++ b/server/src/services/tag.service.spec.ts @@ -1,9 +1,9 @@ import { BadRequestException } from '@nestjs/common'; import { when } from 'jest-when'; -import { TagService } from 'src/domain/tag/tag.service'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { TagType } from 'src/entities/tag.entity'; import { ITagRepository } from 'src/interfaces/tag.repository'; +import { TagService } from 'src/services/tag.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { tagResponseStub, tagStub } from 'test/fixtures/tag.stub'; diff --git a/server/src/domain/tag/tag.service.ts b/server/src/services/tag.service.ts similarity index 100% rename from server/src/domain/tag/tag.service.ts rename to server/src/services/tag.service.ts diff --git a/server/src/domain/trash/trash.service.spec.ts b/server/src/services/trash.service.spec.ts similarity index 98% rename from server/src/domain/trash/trash.service.spec.ts rename to server/src/services/trash.service.spec.ts index 021c8e526..09f668e6f 100644 --- a/server/src/domain/trash/trash.service.spec.ts +++ b/server/src/services/trash.service.spec.ts @@ -1,9 +1,9 @@ import { BadRequestException } from '@nestjs/common'; import { JobName } from 'src/domain/job/job.constants'; -import { TrashService } from 'src/domain/trash/trash.service'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; import { IJobRepository } from 'src/interfaces/job.repository'; +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'; diff --git a/server/src/domain/trash/trash.service.ts b/server/src/services/trash.service.ts similarity index 100% rename from server/src/domain/trash/trash.service.ts rename to server/src/services/trash.service.ts diff --git a/server/src/domain/user/user.service.spec.ts b/server/src/services/user.service.spec.ts similarity index 99% rename from server/src/domain/user/user.service.spec.ts rename to server/src/services/user.service.spec.ts index 804a892c7..51457b201 100644 --- a/server/src/domain/user/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -6,7 +6,6 @@ import { } from '@nestjs/common'; import { when } from 'jest-when'; import { JobName } from 'src/domain/job/job.constants'; -import { UserService } from 'src/domain/user/user.service'; import { UpdateUserDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; import { IAlbumRepository } from 'src/interfaces/album.repository'; @@ -16,6 +15,7 @@ import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; +import { UserService } from 'src/services/user.service'; import { CacheControl, ImmichFileResponse } from 'src/utils'; import { authStub } from 'test/fixtures/auth.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; diff --git a/server/src/domain/user/user.service.ts b/server/src/services/user.service.ts similarity index 100% rename from server/src/domain/user/user.service.ts rename to server/src/services/user.service.ts From 28ad004b01c4f23981e784c08a23811a99d84658 Mon Sep 17 00:00:00 2001 From: Kirill <44521162+kirilldem@users.noreply.github.com> Date: Thu, 21 Mar 2024 03:58:52 +0100 Subject: [PATCH 04/33] Update remote-machine-learning.md (#8038) * Update remote-machine-learning.md provide an example to use cuda or another container * Update docs/docs/guides/remote-machine-learning.md Co-authored-by: aviv926 <51673860+aviv926@users.noreply.github.com> * Update docs/docs/guides/remote-machine-learning.md --------- Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> Co-authored-by: aviv926 <51673860+aviv926@users.noreply.github.com> --- docs/docs/guides/remote-machine-learning.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/docs/guides/remote-machine-learning.md b/docs/docs/guides/remote-machine-learning.md index b7865686c..087f9aab7 100644 --- a/docs/docs/guides/remote-machine-learning.md +++ b/docs/docs/guides/remote-machine-learning.md @@ -16,7 +16,12 @@ version: '3.8' services: immich-machine-learning: container_name: immich_machine_learning + # For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag. + # Example tag: ${IMMICH_VERSION:-release}-cuda image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} + # extends: + # file: hwaccel.ml.yml + # service: # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable volumes: - model-cache:/cache restart: always From 9fd5d2ad9ca8ea7feda7973ee431fad1b03aa538 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:59:15 -0400 Subject: [PATCH 05/33] fix(deps): update machine-learning (#8057) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- machine-learning/export/Dockerfile | 2 +- machine-learning/poetry.lock | 42 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/machine-learning/export/Dockerfile b/machine-learning/export/Dockerfile index 1016b330c..b9aa8f1ed 100644 --- a/machine-learning/export/Dockerfile +++ b/machine-learning/export/Dockerfile @@ -1,4 +1,4 @@ -FROM mambaorg/micromamba:bookworm-slim@sha256:96586e238e2fed914b839e50cf91943b5655262348d141466b34ced2e0b5b155 as builder +FROM mambaorg/micromamba:bookworm-slim@sha256:881dbb68d115182b2c12e7e77dc54ea5005fd4e0123ca009d822adb5b0631785 as builder ENV NODE_ENV=production \ TRANSFORMERS_CACHE=/cache \ diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index 7a0c66bdc..8d53fc84e 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -877,13 +877,13 @@ tqdm = ["tqdm"] [[package]] name = "ftfy" -version = "6.1.3" +version = "6.2.0" description = "Fixes mojibake and other problems with Unicode, after the fact" optional = false python-versions = ">=3.8,<4" files = [ - {file = "ftfy-6.1.3-py3-none-any.whl", hash = "sha256:e49c306c06a97f4986faa7a8740cfe3c13f3106e85bcec73eb629817e671557c"}, - {file = "ftfy-6.1.3.tar.gz", hash = "sha256:693274aead811cff24c1e8784165aa755cd2f6e442a5ec535c7d697f6422a422"}, + {file = "ftfy-6.2.0-py3-none-any.whl", hash = "sha256:f94a2c34b76e07475720e3096f5ca80911d152406fbde66fdb45c4d0c9150026"}, + {file = "ftfy-6.2.0.tar.gz", hash = "sha256:5e42143c7025ef97944ca2619d6b61b0619fc6654f98771d39e862c1424c75c0"}, ] [package.dependencies] @@ -2844,28 +2844,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.2" +version = "0.3.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77f2612752e25f730da7421ca5e3147b213dca4f9a0f7e0b534e9562c5441f01"}, - {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9966b964b2dd1107797be9ca7195002b874424d1d5472097701ae8f43eadef5d"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b83d17ff166aa0659d1e1deaf9f2f14cbe387293a906de09bc4860717eb2e2da"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb875c6cc87b3703aeda85f01c9aebdce3d217aeaca3c2e52e38077383f7268a"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be75e468a6a86426430373d81c041b7605137a28f7014a72d2fc749e47f572aa"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:967978ac2d4506255e2f52afe70dda023fc602b283e97685c8447d036863a302"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1231eacd4510f73222940727ac927bc5d07667a86b0cbe822024dd00343e77e9"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6d613b19e9a8021be2ee1d0e27710208d1603b56f47203d0abbde906929a9b"}, - {file = "ruff-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8439338a6303585d27b66b4626cbde89bb3e50fa3cae86ce52c1db7449330a7"}, - {file = "ruff-0.3.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:de8b480d8379620cbb5ea466a9e53bb467d2fb07c7eca54a4aa8576483c35d36"}, - {file = "ruff-0.3.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b74c3de9103bd35df2bb05d8b2899bf2dbe4efda6474ea9681280648ec4d237d"}, - {file = "ruff-0.3.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f380be9fc15a99765c9cf316b40b9da1f6ad2ab9639e551703e581a5e6da6745"}, - {file = "ruff-0.3.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0ac06a3759c3ab9ef86bbeca665d31ad3aa9a4b1c17684aadb7e61c10baa0df4"}, - {file = "ruff-0.3.2-py3-none-win32.whl", hash = "sha256:9bd640a8f7dd07a0b6901fcebccedadeb1a705a50350fb86b4003b805c81385a"}, - {file = "ruff-0.3.2-py3-none-win_amd64.whl", hash = "sha256:0c1bdd9920cab5707c26c8b3bf33a064a4ca7842d91a99ec0634fec68f9f4037"}, - {file = "ruff-0.3.2-py3-none-win_arm64.whl", hash = "sha256:5f65103b1d76e0d600cabd577b04179ff592064eaa451a70a81085930e907d0b"}, - {file = "ruff-0.3.2.tar.gz", hash = "sha256:fa78ec9418eb1ca3db392811df3376b46471ae93792a81af2d1cbb0e5dcb5142"}, + {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:973a0e388b7bc2e9148c7f9be8b8c6ae7471b9be37e1cc732f8f44a6f6d7720d"}, + {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa60d23269d6e2031129b053fdb4e5a7b0637fc6c9c0586737b962b2f834493"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eca7ff7a47043cf6ce5c7f45f603b09121a7cc047447744b029d1b719278eb5"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7d3f6762217c1da954de24b4a1a70515630d29f71e268ec5000afe81377642d"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b24c19e8598916d9c6f5a5437671f55ee93c212a2c4c569605dc3842b6820386"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5a6cbf216b69c7090f0fe4669501a27326c34e119068c1494f35aaf4cc683778"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352e95ead6964974b234e16ba8a66dad102ec7bf8ac064a23f95371d8b198aab"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d6ab88c81c4040a817aa432484e838aaddf8bfd7ca70e4e615482757acb64f8"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79bca3a03a759cc773fca69e0bdeac8abd1c13c31b798d5bb3c9da4a03144a9f"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2700a804d5336bcffe063fd789ca2c7b02b552d2e323a336700abb8ae9e6a3f8"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd66469f1a18fdb9d32e22b79f486223052ddf057dc56dea0caaf1a47bdfaf4e"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45817af234605525cdf6317005923bf532514e1ea3d9270acf61ca2440691376"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0da458989ce0159555ef224d5b7c24d3d2e4bf4c300b85467b08c3261c6bc6a8"}, + {file = "ruff-0.3.3-py3-none-win32.whl", hash = "sha256:f2831ec6a580a97f1ea82ea1eda0401c3cdf512cf2045fa3c85e8ef109e87de0"}, + {file = "ruff-0.3.3-py3-none-win_amd64.whl", hash = "sha256:be90bcae57c24d9f9d023b12d627e958eb55f595428bafcb7fec0791ad25ddfc"}, + {file = "ruff-0.3.3-py3-none-win_arm64.whl", hash = "sha256:0171aab5fecdc54383993389710a3d1227f2da124d76a2784a7098e818f92d61"}, + {file = "ruff-0.3.3.tar.gz", hash = "sha256:38671be06f57a2f8aba957d9f701ea889aa5736be806f18c0cd03d6ff0cbca8d"}, ] [[package]] From 16d0df796c3dec465096db83d409aef85c1185bd Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 20 Mar 2024 22:15:09 -0500 Subject: [PATCH 06/33] refactor: infra folder (#8138) --- .github/workflows/test.yml | 2 +- docs/docs/developer/database-migrations.md | 2 +- server/package.json | 23 +-- server/src/apps/api.main.ts | 8 +- server/src/apps/api.service.ts | 6 +- server/src/apps/app.module.ts | 6 +- server/src/apps/microservices.main.ts | 8 +- server/src/apps/microservices.service.ts | 5 +- server/src/config.ts | 2 +- server/src/constants.ts | 105 ++++++++++ server/src/controllers/auth.controller.ts | 2 +- .../src/controllers/shared-link.controller.ts | 2 +- server/src/cores/access.core.ts | 2 +- server/src/cores/storage.core.ts | 4 +- server/src/cores/system-config.core.ts | 4 +- server/src/{infra => }/database.config.ts | 6 +- server/src/decorators.ts | 2 +- server/src/domain/auth/auth.constant.ts | 12 -- server/src/domain/job/job.constants.ts | 155 --------------- server/src/domain/job/job.interface.ts | 41 ---- server/src/domain/media/media.constant.ts | 1 - .../domain/smart-info/smart-info.constant.ts | 129 ------------- .../system-config/system-config.constants.ts | 27 --- server/src/dtos/job.dto.ts | 2 +- server/src/dtos/server-info.dto.ts | 2 +- server/src/dtos/system-config-job.dto.ts | 2 +- server/src/entities/system-config.entity.ts | 2 +- .../immich/api-v1/asset/asset-repository.ts | 2 +- .../immich/api-v1/asset/asset.service.spec.ts | 3 +- .../src/immich/api-v1/asset/asset.service.ts | 9 +- server/src/immich/app.utils.ts | 8 +- server/src/infra/sql-generator/sql.logger.ts | 25 --- server/src/interfaces/asset.repository.ts | 2 +- server/src/interfaces/database.repository.ts | 2 +- server/src/interfaces/job.repository.ts | 147 ++++++++++++-- server/src/interfaces/person.repository.ts | 2 +- server/src/interfaces/search.repository.ts | 2 +- server/src/middleware/auth.guard.ts | 4 +- server/src/middleware/error.interceptor.ts | 4 +- .../src/middleware/file-upload.interceptor.ts | 2 +- .../websocket.adapter.ts | 0 .../migrations/1700713871511-UsePgVectors.ts | 4 +- .../1700713994428-AddCLIPEmbeddingIndex.ts | 2 +- .../1700714033632-AddFaceEmbeddingIndex.ts | 2 +- .../sql => queries}/access.repository.sql | 0 .../sql => queries}/album.repository.sql | 0 .../sql => queries}/api.key.repository.sql | 0 .../sql => queries}/asset.repository.sql | 0 .../sql => queries}/audit.repository.sql | 0 .../sql => queries}/library.repository.sql | 0 .../sql => queries}/move.repository.sql | 0 .../sql => queries}/partner.repository.sql | 0 .../sql => queries}/person.repository.sql | 0 .../sql => queries}/search.repository.sql | 0 .../shared.link.repository.sql | 0 .../system.config.repository.sql | 0 .../system.metadata.repository.sql | 0 .../{infra/sql => queries}/tag.repository.sql | 0 .../sql => queries}/user.repository.sql | 0 .../sql => queries}/user.token.repository.sql | 0 server/src/repositories/access.repository.ts | 2 +- .../src/repositories/activity.repository.ts | 2 +- server/src/repositories/album.repository.ts | 6 +- server/src/repositories/api-key.repository.ts | 2 +- .../repositories/asset-stack.repository.ts | 2 +- server/src/repositories/asset.repository.ts | 6 +- server/src/repositories/audit.repository.ts | 2 +- .../repositories/communication.repository.ts | 4 +- server/src/repositories/crypto.repository.ts | 2 +- .../src/repositories/database.repository.ts | 8 +- .../src/repositories/filesystem.provider.ts | 6 +- server/src/repositories/job.repository.ts | 76 +++++++- server/src/repositories/library.repository.ts | 2 +- .../machine-learning.repository.ts | 2 +- server/src/repositories/media.repository.ts | 6 +- .../src/repositories/metadata.repository.ts | 12 +- server/src/repositories/move.repository.ts | 2 +- server/src/repositories/partner.repository.ts | 2 +- server/src/repositories/person.repository.ts | 6 +- server/src/repositories/search.repository.ts | 12 +- .../repositories/server-info.repository.ts | 2 +- .../repositories/shared-link.repository.ts | 2 +- .../repositories/system-config.repository.ts | 2 +- .../system-metadata.repository.ts | 2 +- server/src/repositories/tag.repository.ts | 2 +- .../src/repositories/user-token.repository.ts | 2 +- server/src/repositories/user.repository.ts | 2 +- server/src/services/album.service.ts | 2 +- server/src/services/asset.service.spec.ts | 3 +- server/src/services/asset.service.ts | 18 +- server/src/services/audit.service.ts | 9 +- server/src/services/auth.service.spec.ts | 2 +- server/src/services/auth.service.ts | 12 +- server/src/services/database.service.spec.ts | 4 +- server/src/services/database.service.ts | 4 +- server/src/services/download.service.spec.ts | 2 +- server/src/services/download.service.ts | 6 +- server/src/services/job.service.spec.ts | 11 +- server/src/services/job.service.ts | 15 +- server/src/services/library.service.spec.ts | 4 +- server/src/services/library.service.ts | 20 +- server/src/services/media.service.spec.ts | 3 +- server/src/services/media.service.ts | 37 ++-- server/src/services/metadata.service.spec.ts | 3 +- server/src/services/metadata.service.ts | 18 +- server/src/services/person.service.spec.ts | 5 +- server/src/services/person.service.ts | 23 ++- .../src/services/server-info.service.spec.ts | 2 +- server/src/services/server-info.service.ts | 8 +- server/src/services/shared-link.service.ts | 2 +- .../src/services/smart-info.service.spec.ts | 17 +- server/src/services/smart-info.service.ts | 16 +- .../src/services/storage-template.service.ts | 15 +- server/src/services/storage.service.ts | 5 +- .../services/system-config.service.spec.ts | 4 +- server/src/services/system-config.service.ts | 6 +- server/src/services/trash.service.spec.ts | 3 +- server/src/services/trash.service.ts | 5 +- server/src/services/user.service.spec.ts | 5 +- server/src/services/user.service.ts | 8 +- server/src/utils.spec.ts | 103 ---------- server/src/utils.ts | 181 ------------------ server/src/utils/bytes.ts | 24 +++ .../infra.utils.ts => utils/database.ts} | 54 +----- server/src/utils/file.ts | 25 +++ .../src/{infra => utils}/instrumentation.ts | 2 +- server/src/{infra => utils}/logger.ts | 0 .../media/media.util.ts => utils/media.ts} | 0 .../mime-types.spec.ts} | 73 +------ .../mime-types.ts} | 93 +-------- server/src/utils/misc.ts | 32 ++++ server/src/utils/pagination.ts | 79 ++++++++ server/src/utils/set.ts | 36 ++++ .../sql-generator/index.ts => utils/sql.ts} | 30 ++- server/src/utils/version.spec.ts | 72 +++++++ server/src/utils/version.ts | 64 +++++++ server/test/fixtures/library.stub.ts | 2 +- .../repositories/database.repository.mock.ts | 2 +- server/test/utils.ts | 5 +- 139 files changed, 968 insertions(+), 1164 deletions(-) create mode 100644 server/src/constants.ts rename server/src/{infra => }/database.config.ts (86%) delete mode 100644 server/src/domain/auth/auth.constant.ts delete mode 100644 server/src/domain/job/job.constants.ts delete mode 100644 server/src/domain/job/job.interface.ts delete mode 100644 server/src/domain/media/media.constant.ts delete mode 100644 server/src/domain/smart-info/smart-info.constant.ts delete mode 100644 server/src/domain/system-config/system-config.constants.ts delete mode 100644 server/src/infra/sql-generator/sql.logger.ts rename server/src/{infra => middleware}/websocket.adapter.ts (100%) rename server/src/{infra/sql => queries}/access.repository.sql (100%) rename server/src/{infra/sql => queries}/album.repository.sql (100%) rename server/src/{infra/sql => queries}/api.key.repository.sql (100%) rename server/src/{infra/sql => queries}/asset.repository.sql (100%) rename server/src/{infra/sql => queries}/audit.repository.sql (100%) rename server/src/{infra/sql => queries}/library.repository.sql (100%) rename server/src/{infra/sql => queries}/move.repository.sql (100%) rename server/src/{infra/sql => queries}/partner.repository.sql (100%) rename server/src/{infra/sql => queries}/person.repository.sql (100%) rename server/src/{infra/sql => queries}/search.repository.sql (100%) rename server/src/{infra/sql => queries}/shared.link.repository.sql (100%) rename server/src/{infra/sql => queries}/system.config.repository.sql (100%) rename server/src/{infra/sql => queries}/system.metadata.repository.sql (100%) rename server/src/{infra/sql => queries}/tag.repository.sql (100%) rename server/src/{infra/sql => queries}/user.repository.sql (100%) rename server/src/{infra/sql => queries}/user.token.repository.sql (100%) delete mode 100644 server/src/utils.spec.ts delete mode 100644 server/src/utils.ts create mode 100644 server/src/utils/bytes.ts rename server/src/{infra/infra.utils.ts => utils/database.ts} (75%) create mode 100644 server/src/utils/file.ts rename server/src/{infra => utils}/instrumentation.ts (98%) rename server/src/{infra => utils}/logger.ts (100%) rename server/src/{domain/media/media.util.ts => utils/media.ts} (100%) rename server/src/{domain/domain.constant.spec.ts => utils/mime-types.spec.ts} (68%) rename server/src/{domain/domain.constant.ts => utils/mime-types.ts} (56%) create mode 100644 server/src/utils/misc.ts create mode 100644 server/src/utils/pagination.ts create mode 100644 server/src/utils/set.ts rename server/src/{infra/sql-generator/index.ts => utils/sql.ts} (90%) create mode 100644 server/src/utils/version.spec.ts create mode 100644 server/src/utils/version.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13f5a73a1..6a5df111d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -354,7 +354,7 @@ jobs: id: verify-changed-sql-files with: files: | - server/src/infra/sql + server/src/queries - name: Verify SQL files have not changed if: steps.verify-changed-sql-files.outputs.files_changed == 'true' diff --git a/docs/docs/developer/database-migrations.md b/docs/docs/developer/database-migrations.md index eccf78b44..2cddf5f38 100644 --- a/docs/docs/developer/database-migrations.md +++ b/docs/docs/developer/database-migrations.md @@ -5,7 +5,7 @@ After making any changes in the `server/src/entities`, a database migration need 1. Run the command ```bash -npm run typeorm:migrations:generate ./src/infra/ +npm run typeorm:migrations:generate ``` 2. Check if the migration file makes sense. diff --git a/server/package.json b/server/package.json index eb65eadf0..4776e418f 100644 --- a/server/package.json +++ b/server/package.json @@ -25,12 +25,12 @@ "e2e:jobs": "jest --config e2e/jobs/jest-e2e.json --runInBand", "typeorm": "typeorm", "typeorm:migrations:create": "typeorm migration:create", - "typeorm:migrations:generate": "typeorm migration:generate -d ./dist/infra/database.config.js", - "typeorm:migrations:run": "typeorm migration:run -d ./dist/infra/database.config.js", - "typeorm:migrations:revert": "typeorm migration:revert -d ./dist/infra/database.config.js", - "typeorm:schema:drop": "typeorm query -d ./dist/infra/database.config.js 'DROP schema public cascade; CREATE schema public;'", + "typeorm:migrations:generate": "typeorm migration:generate -d ./dist/database.config.js", + "typeorm:migrations:run": "typeorm migration:run -d ./dist/database.config.js", + "typeorm:migrations:revert": "typeorm migration:revert -d ./dist/database.config.js", + "typeorm:schema:drop": "typeorm query -d ./dist/database.config.js 'DROP schema public cascade; CREATE schema public;'", "typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run", - "sql:generate": "node ./dist/infra/sql-generator/" + "sql:generate": "node ./dist/utils/sql.js" }, "dependencies": { "@babel/runtime": "^7.22.11", @@ -146,15 +146,16 @@ "^.+\\.ts$": "ts-jest" }, "collectCoverageFrom": [ - "/src/**/*.(t|j)s", - "!/src/infra/**/*", - "!/src/migrations/**/*", - "!/src/subscribers/**/*", - "!/src/immich/controllers/**/*" + "/src/cores/*.(t|j)s", + "/src/dtos/*.(t|j)s", + "/src/interfaces/*.(t|j)s", + "/src/services/*.(t|j)s", + "/src/utils/*.(t|j)s", + "/src/*.t|j)s" ], "coverageDirectory": "./coverage", "coverageThreshold": { - "./src/domain/": { + "./src/": { "branches": 75, "functions": 80, "lines": 85, diff --git a/server/src/apps/api.main.ts b/server/src/apps/api.main.ts index 9ffdd1d48..07685308e 100644 --- a/server/src/apps/api.main.ts +++ b/server/src/apps/api.main.ts @@ -7,11 +7,11 @@ import sirv from 'sirv'; import { ApiModule } from 'src/apps/api.module'; import { ApiService } from 'src/apps/api.service'; import { excludePaths } from 'src/config'; -import { WEB_ROOT, envName, isDev, serverVersion } from 'src/domain/domain.constant'; +import { WEB_ROOT, envName, isDev, serverVersion } from 'src/constants'; import { useSwagger } from 'src/immich/app.utils'; -import { otelSDK } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; -import { WebSocketAdapter } from 'src/infra/websocket.adapter'; +import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; +import { otelSDK } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; const logger = new ImmichLogger('ImmichServer'); const port = Number(process.env.SERVER_PORT) || 3001; diff --git a/server/src/apps/api.service.ts b/server/src/apps/api.service.ts index fbd0d0e4a..87107b23f 100644 --- a/server/src/apps/api.service.ts +++ b/server/src/apps/api.service.ts @@ -3,8 +3,7 @@ import { Cron, CronExpression, Interval } from '@nestjs/schedule'; import { NextFunction, Request, Response } from 'express'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; -import { ONE_HOUR, WEB_ROOT } from 'src/domain/domain.constant'; -import { ImmichLogger } from 'src/infra/logger'; +import { ONE_HOUR, WEB_ROOT } from 'src/constants'; import { AuthService } from 'src/services/auth.service'; import { DatabaseService } from 'src/services/database.service'; import { JobService } from 'src/services/job.service'; @@ -12,7 +11,8 @@ import { ServerInfoService } from 'src/services/server-info.service'; import { SharedLinkService } from 'src/services/shared-link.service'; import { StorageService } from 'src/services/storage.service'; import { SystemConfigService } from 'src/services/system-config.service'; -import { OpenGraphTags } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { OpenGraphTags } from 'src/utils/misc'; const render = (index: string, meta: OpenGraphTags) => { const tags = ` diff --git a/server/src/apps/app.module.ts b/server/src/apps/app.module.ts index 964e2d4e1..14aae51d9 100644 --- a/server/src/apps/app.module.ts +++ b/server/src/apps/app.module.ts @@ -6,10 +6,8 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; import { OpenTelemetryModule } from 'nestjs-otel'; import { bullConfig, bullQueues, immichAppConfig } from 'src/config'; +import { databaseConfig } from 'src/database.config'; import { databaseEntities } from 'src/entities'; -import { databaseConfig } from 'src/infra/database.config'; -import { otelConfig } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IActivityRepository } from 'src/interfaces/activity.repository'; import { IAlbumRepository } from 'src/interfaces/album.repository'; @@ -88,6 +86,8 @@ import { SystemConfigService } from 'src/services/system-config.service'; import { TagService } from 'src/services/tag.service'; import { TrashService } from 'src/services/trash.service'; import { UserService } from 'src/services/user.service'; +import { otelConfig } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; const services: Provider[] = [ APIKeyService, diff --git a/server/src/apps/microservices.main.ts b/server/src/apps/microservices.main.ts index 552fb714c..d35483d72 100644 --- a/server/src/apps/microservices.main.ts +++ b/server/src/apps/microservices.main.ts @@ -1,9 +1,9 @@ import { NestFactory } from '@nestjs/core'; import { MicroservicesModule } from 'src/apps/microservices.module'; -import { envName, serverVersion } from 'src/domain/domain.constant'; -import { otelSDK } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; -import { WebSocketAdapter } from 'src/infra/websocket.adapter'; +import { envName, serverVersion } from 'src/constants'; +import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; +import { otelSDK } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; const logger = new ImmichLogger('ImmichMicroservice'); const port = Number(process.env.MICROSERVICES_PORT) || 3002; diff --git a/server/src/apps/microservices.service.ts b/server/src/apps/microservices.service.ts index 8f11583be..a6b7f3869 100644 --- a/server/src/apps/microservices.service.ts +++ b/server/src/apps/microservices.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { JobName } from 'src/domain/job/job.constants'; -import { IDeleteFilesJob } from 'src/domain/job/job.interface'; -import { otelSDK } from 'src/infra/instrumentation'; +import { IDeleteFilesJob, JobName } from 'src/interfaces/job.repository'; import { AssetService } from 'src/services/asset.service'; import { AuditService } from 'src/services/audit.service'; import { DatabaseService } from 'src/services/database.service'; @@ -15,6 +13,7 @@ import { StorageTemplateService } from 'src/services/storage-template.service'; import { StorageService } from 'src/services/storage.service'; import { SystemConfigService } from 'src/services/system-config.service'; import { UserService } from 'src/services/user.service'; +import { otelSDK } from 'src/utils/instrumentation'; @Injectable() export class MicroservicesService { diff --git a/server/src/config.ts b/server/src/config.ts index b97f0bb7e..f19546093 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -3,8 +3,8 @@ import { ConfigModuleOptions } from '@nestjs/config'; import { QueueOptions } from 'bullmq'; import { RedisOptions } from 'ioredis'; import Joi from 'joi'; -import { QueueName } from 'src/domain/job/job.constants'; import { LogLevel } from 'src/entities/system-config.entity'; +import { QueueName } from 'src/interfaces/job.repository'; const WHEN_DB_URL_SET = Joi.when('DB_URL', { is: Joi.exist(), diff --git a/server/src/constants.ts b/server/src/constants.ts new file mode 100644 index 000000000..eaacf12d9 --- /dev/null +++ b/server/src/constants.ts @@ -0,0 +1,105 @@ +import { Duration } from 'luxon'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { Version } from 'src/utils/version'; + +const { version } = JSON.parse(readFileSync('./package.json', 'utf8')); +export const serverVersion = Version.fromString(version); + +export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); +export const ONE_HOUR = Duration.fromObject({ hours: 1 }); + +export const envName = (process.env.NODE_ENV || 'development').toUpperCase(); +export const isDev = process.env.NODE_ENV === 'development'; +export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; +export const WEB_ROOT = process.env.IMMICH_WEB_ROOT || '/usr/src/app/www'; + +const GEODATA_ROOT_PATH = process.env.IMMICH_REVERSE_GEOCODING_ROOT || '/usr/src/resources'; + +export const citiesFile = 'cities500.txt'; +export const geodataDatePath = join(GEODATA_ROOT_PATH, 'geodata-date.txt'); +export const geodataAdmin1Path = join(GEODATA_ROOT_PATH, 'admin1CodesASCII.txt'); +export const geodataAdmin2Path = join(GEODATA_ROOT_PATH, 'admin2Codes.txt'); +export const geodataCities500Path = join(GEODATA_ROOT_PATH, citiesFile); + +export const MOBILE_REDIRECT = 'app.immich:/'; +export const LOGIN_URL = '/auth/login?autoLaunch=0'; +export const IMMICH_ACCESS_COOKIE = 'immich_access_token'; +export const IMMICH_IS_AUTHENTICATED = 'immich_is_authenticated'; +export const IMMICH_AUTH_TYPE_COOKIE = 'immich_auth_type'; +export const IMMICH_API_KEY_NAME = 'api_key'; +export const IMMICH_API_KEY_HEADER = 'x-api-key'; +export const IMMICH_SHARED_LINK_ACCESS_COOKIE = 'immich_shared_link_token'; +export enum AuthType { + PASSWORD = 'password', + OAUTH = 'oauth', +} + +export const FACE_THUMBNAIL_SIZE = 250; + +export const supportedYearTokens = ['y', 'yy']; +export const supportedMonthTokens = ['M', 'MM', 'MMM', 'MMMM']; +export const supportedWeekTokens = ['W', 'WW']; +export const supportedDayTokens = ['d', 'dd']; +export const supportedHourTokens = ['h', 'hh', 'H', 'HH']; +export const supportedMinuteTokens = ['m', 'mm']; +export const supportedSecondTokens = ['s', 'ss', 'SSS']; +export const supportedPresetTokens = [ + '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', + '{{y}}/{{MM}}-{{dd}}/{{filename}}', + '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', + '{{y}}/{{MM}}/{{filename}}', + '{{y}}/{{MMM}}/{{filename}}', + '{{y}}/{{MMMM}}/{{filename}}', + '{{y}}/{{MM}}/{{dd}}/{{filename}}', + '{{y}}/{{MMMM}}/{{dd}}/{{filename}}', + '{{y}}/{{y}}-{{MM}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', + '{{y}}-{{MM}}-{{dd}}/{{filename}}', + '{{y}}-{{MMM}}-{{dd}}/{{filename}}', + '{{y}}-{{MMMM}}-{{dd}}/{{filename}}', + '{{y}}/{{y}}-{{MM}}/{{filename}}', + '{{y}}/{{y}}-{{WW}}/{{filename}}', + '{{y}}/{{y}}-{{MM}}-{{dd}}/{{assetId}}', + '{{y}}/{{y}}-{{MM}}/{{assetId}}', + '{{y}}/{{y}}-{{WW}}/{{assetId}}', + '{{album}}/{{filename}}', +]; + +type ModelInfo = { dimSize: number }; +export const CLIP_MODEL_INFO: Record = { + RN50__openai: { dimSize: 1024 }, + RN50__yfcc15m: { dimSize: 1024 }, + RN50__cc12m: { dimSize: 1024 }, + RN101__openai: { dimSize: 512 }, + RN101__yfcc15m: { dimSize: 512 }, + RN50x4__openai: { dimSize: 640 }, + RN50x16__openai: { dimSize: 768 }, + RN50x64__openai: { dimSize: 1024 }, + 'ViT-B-32__openai': { dimSize: 512 }, + 'ViT-B-32__laion2b_e16': { dimSize: 512 }, + 'ViT-B-32__laion400m_e31': { dimSize: 512 }, + 'ViT-B-32__laion400m_e32': { dimSize: 512 }, + 'ViT-B-32__laion2b-s34b-b79k': { dimSize: 512 }, + 'ViT-B-16__openai': { dimSize: 512 }, + 'ViT-B-16__laion400m_e31': { dimSize: 512 }, + 'ViT-B-16__laion400m_e32': { dimSize: 512 }, + 'ViT-B-16-plus-240__laion400m_e31': { dimSize: 640 }, + 'ViT-B-16-plus-240__laion400m_e32': { dimSize: 640 }, + 'ViT-L-14__openai': { dimSize: 768 }, + 'ViT-L-14__laion400m_e31': { dimSize: 768 }, + 'ViT-L-14__laion400m_e32': { dimSize: 768 }, + 'ViT-L-14__laion2b-s32b-b82k': { dimSize: 768 }, + 'ViT-L-14-336__openai': { dimSize: 768 }, + 'ViT-L-14-quickgelu__dfn2b': { dimSize: 768 }, + 'ViT-H-14__laion2b-s32b-b79k': { dimSize: 1024 }, + 'ViT-H-14-quickgelu__dfn5b': { dimSize: 1024 }, + 'ViT-H-14-378-quickgelu__dfn5b': { dimSize: 1024 }, + 'ViT-g-14__laion2b-s12b-b42k': { dimSize: 1024 }, + 'LABSE-Vit-L-14': { dimSize: 768 }, + 'XLM-Roberta-Large-Vit-B-32': { dimSize: 512 }, + 'XLM-Roberta-Large-Vit-B-16Plus': { dimSize: 640 }, + 'XLM-Roberta-Large-Vit-L-14': { dimSize: 768 }, + 'XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k': { dimSize: 1024 }, + 'nllb-clip-base-siglip__v1': { dimSize: 768 }, + 'nllb-clip-large-siglip__v1': { dimSize: 1152 }, +}; diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index cbb649dc0..9b4e7a3bc 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; -import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/domain/auth/auth.constant'; +import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/constants'; import { AuthDeviceResponseDto, AuthDto, diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 61dd6605a..990f4e322 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; -import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/domain/auth/auth.constant'; +import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/constants'; import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; diff --git a/server/src/cores/access.core.ts b/server/src/cores/access.core.ts index 1c0b1dabb..78b30e4b1 100644 --- a/server/src/cores/access.core.ts +++ b/server/src/cores/access.core.ts @@ -2,7 +2,7 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; import { AuthDto } from 'src/dtos/auth.dto'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; -import { setDifference, setIsEqual, setUnion } from 'src/utils'; +import { setDifference, setIsEqual, setUnion } from 'src/utils/set'; export enum Permission { ACTIVITY_CREATE = 'activity.create', diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 8bf65b248..17f3f0360 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,16 +1,16 @@ import { dirname, join, resolve } from 'node:path'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { APP_MEDIA_LOCATION } from 'src/domain/domain.constant'; import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { ImmichLogger } from 'src/utils/logger'; export enum StorageFolder { ENCODED_VIDEO = 'encoded-video', diff --git a/server/src/cores/system-config.core.ts b/server/src/cores/system-config.core.ts index a6692fa18..6554839a5 100644 --- a/server/src/cores/system-config.core.ts +++ b/server/src/cores/system-config.core.ts @@ -5,7 +5,6 @@ import { validate } from 'class-validator'; import { load as loadYaml } from 'js-yaml'; import * as _ from 'lodash'; import { Subject } from 'rxjs'; -import { QueueName } from 'src/domain/job/job.constants'; import { SystemConfigDto } from 'src/dtos/system-config.dto'; import { AudioCodec, @@ -21,8 +20,9 @@ import { TranscodePolicy, VideoCodec, } from 'src/entities/system-config.entity'; -import { ImmichLogger } from 'src/infra/logger'; +import { QueueName } from 'src/interfaces/job.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { ImmichLogger } from 'src/utils/logger'; export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise; diff --git a/server/src/infra/database.config.ts b/server/src/database.config.ts similarity index 86% rename from server/src/infra/database.config.ts rename to server/src/database.config.ts index 572288c59..e4e251ffc 100644 --- a/server/src/infra/database.config.ts +++ b/server/src/database.config.ts @@ -16,9 +16,9 @@ const urlOrParts = url /* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/ export const databaseConfig: PostgresConnectionOptions = { type: 'postgres', - entities: [__dirname + '/../entities/*.entity.{js,ts}'], - migrations: [__dirname + '/../migrations/*.{js,ts}'], - subscribers: [__dirname + '/../subscribers/*.{js,ts}'], + entities: [__dirname + '/entities/*.entity.{js,ts}'], + migrations: [__dirname + '/migrations/*.{js,ts}'], + subscribers: [__dirname + '/subscribers/*.{js,ts}'], migrationsRun: false, synchronize: false, connectTimeoutMS: 10_000, // 10 seconds diff --git a/server/src/decorators.ts b/server/src/decorators.ts index 06dc0bfdc..33efdafa3 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -1,6 +1,6 @@ import { SetMetadata } from '@nestjs/common'; import _ from 'lodash'; -import { setUnion } from 'src/utils'; +import { setUnion } from 'src/utils/set'; // PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the // maximum number of parameters is 65535. Any query that tries to bind more than that (e.g. searching diff --git a/server/src/domain/auth/auth.constant.ts b/server/src/domain/auth/auth.constant.ts deleted file mode 100644 index f29fc9274..000000000 --- a/server/src/domain/auth/auth.constant.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const MOBILE_REDIRECT = 'app.immich:/'; -export const LOGIN_URL = '/auth/login?autoLaunch=0'; -export const IMMICH_ACCESS_COOKIE = 'immich_access_token'; -export const IMMICH_IS_AUTHENTICATED = 'immich_is_authenticated'; -export const IMMICH_AUTH_TYPE_COOKIE = 'immich_auth_type'; -export const IMMICH_API_KEY_NAME = 'api_key'; -export const IMMICH_API_KEY_HEADER = 'x-api-key'; -export const IMMICH_SHARED_LINK_ACCESS_COOKIE = 'immich_shared_link_token'; -export enum AuthType { - PASSWORD = 'password', - OAUTH = 'oauth', -} diff --git a/server/src/domain/job/job.constants.ts b/server/src/domain/job/job.constants.ts deleted file mode 100644 index fe3c81727..000000000 --- a/server/src/domain/job/job.constants.ts +++ /dev/null @@ -1,155 +0,0 @@ -export enum QueueName { - THUMBNAIL_GENERATION = 'thumbnailGeneration', - METADATA_EXTRACTION = 'metadataExtraction', - VIDEO_CONVERSION = 'videoConversion', - FACE_DETECTION = 'faceDetection', - FACIAL_RECOGNITION = 'facialRecognition', - SMART_SEARCH = 'smartSearch', - BACKGROUND_TASK = 'backgroundTask', - STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration', - MIGRATION = 'migration', - SEARCH = 'search', - SIDECAR = 'sidecar', - LIBRARY = 'library', -} - -export type ConcurrentQueueName = Exclude< - QueueName, - QueueName.STORAGE_TEMPLATE_MIGRATION | QueueName.FACIAL_RECOGNITION ->; - -export enum JobCommand { - START = 'start', - PAUSE = 'pause', - RESUME = 'resume', - EMPTY = 'empty', - CLEAR_FAILED = 'clear-failed', -} - -export enum JobName { - // conversion - QUEUE_VIDEO_CONVERSION = 'queue-video-conversion', - VIDEO_CONVERSION = 'video-conversion', - - // thumbnails - QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails', - GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail', - GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail', - GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail', - GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail', - - // metadata - QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction', - METADATA_EXTRACTION = 'metadata-extraction', - LINK_LIVE_PHOTOS = 'link-live-photos', - - // user - USER_DELETION = 'user-deletion', - USER_DELETE_CHECK = 'user-delete-check', - USER_SYNC_USAGE = 'user-sync-usage', - - // asset - ASSET_DELETION = 'asset-deletion', - ASSET_DELETION_CHECK = 'asset-deletion-check', - - // storage template - STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration', - STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single', - - // migration - QUEUE_MIGRATION = 'queue-migration', - MIGRATE_ASSET = 'migrate-asset', - MIGRATE_PERSON = 'migrate-person', - - // facial recognition - PERSON_CLEANUP = 'person-cleanup', - QUEUE_FACE_DETECTION = 'queue-face-detection', - FACE_DETECTION = 'face-detection', - QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition', - FACIAL_RECOGNITION = 'facial-recognition', - - // library managment - LIBRARY_SCAN = 'library-refresh', - LIBRARY_SCAN_ASSET = 'library-refresh-asset', - LIBRARY_REMOVE_OFFLINE = 'library-remove-offline', - LIBRARY_DELETE = 'library-delete', - LIBRARY_QUEUE_SCAN_ALL = 'library-queue-all-refresh', - LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup', - - // cleanup - DELETE_FILES = 'delete-files', - CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs', - - // smart search - QUEUE_SMART_SEARCH = 'queue-smart-search', - SMART_SEARCH = 'smart-search', - - // XMP sidecars - QUEUE_SIDECAR = 'queue-sidecar', - SIDECAR_DISCOVERY = 'sidecar-discovery', - SIDECAR_SYNC = 'sidecar-sync', - SIDECAR_WRITE = 'sidecar-write', -} - -export const JOBS_ASSET_PAGINATION_SIZE = 1000; - -export const JOBS_TO_QUEUE: Record = { - // misc - [JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK, - [JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK, - [JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK, - [JobName.USER_DELETION]: QueueName.BACKGROUND_TASK, - [JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK, - [JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK, - [JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK, - [JobName.USER_SYNC_USAGE]: QueueName.BACKGROUND_TASK, - - // conversion - [JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION, - [JobName.VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION, - - // thumbnails - [JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION, - [JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, - [JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, - [JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, - [JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, - - // metadata - [JobName.QUEUE_METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION, - [JobName.METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION, - [JobName.LINK_LIVE_PHOTOS]: QueueName.METADATA_EXTRACTION, - - // storage template - [JobName.STORAGE_TEMPLATE_MIGRATION]: QueueName.STORAGE_TEMPLATE_MIGRATION, - [JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: QueueName.STORAGE_TEMPLATE_MIGRATION, - - // migration - [JobName.QUEUE_MIGRATION]: QueueName.MIGRATION, - [JobName.MIGRATE_ASSET]: QueueName.MIGRATION, - [JobName.MIGRATE_PERSON]: QueueName.MIGRATION, - - // facial recognition - [JobName.QUEUE_FACE_DETECTION]: QueueName.FACE_DETECTION, - [JobName.FACE_DETECTION]: QueueName.FACE_DETECTION, - [JobName.QUEUE_FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION, - [JobName.FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION, - - // smart search - [JobName.QUEUE_SMART_SEARCH]: QueueName.SMART_SEARCH, - [JobName.SMART_SEARCH]: QueueName.SMART_SEARCH, - - // XMP sidecars - [JobName.QUEUE_SIDECAR]: QueueName.SIDECAR, - [JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR, - [JobName.SIDECAR_SYNC]: QueueName.SIDECAR, - [JobName.SIDECAR_WRITE]: QueueName.SIDECAR, - - // Library management - [JobName.LIBRARY_SCAN_ASSET]: QueueName.LIBRARY, - [JobName.LIBRARY_SCAN]: QueueName.LIBRARY, - [JobName.LIBRARY_DELETE]: QueueName.LIBRARY, - [JobName.LIBRARY_REMOVE_OFFLINE]: QueueName.LIBRARY, - [JobName.LIBRARY_QUEUE_SCAN_ALL]: QueueName.LIBRARY, - [JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY, -}; diff --git a/server/src/domain/job/job.interface.ts b/server/src/domain/job/job.interface.ts deleted file mode 100644 index 8e7420bf9..000000000 --- a/server/src/domain/job/job.interface.ts +++ /dev/null @@ -1,41 +0,0 @@ -export interface IBaseJob { - force?: boolean; -} - -export interface IEntityJob extends IBaseJob { - id: string; - source?: 'upload' | 'sidecar-write'; -} - -export interface IAssetDeletionJob extends IEntityJob { - fromExternal?: boolean; -} - -export interface ILibraryFileJob extends IEntityJob { - ownerId: string; - assetPath: string; -} - -export interface ILibraryRefreshJob extends IEntityJob { - refreshModifiedFiles: boolean; - refreshAllFiles: boolean; -} - -export interface IBulkEntityJob extends IBaseJob { - ids: string[]; -} - -export interface IDeleteFilesJob extends IBaseJob { - files: Array; -} - -export interface ISidecarWriteJob extends IEntityJob { - description?: string; - dateTimeOriginal?: string; - latitude?: number; - longitude?: number; -} - -export interface IDeferrableJob extends IEntityJob { - deferred?: boolean; -} diff --git a/server/src/domain/media/media.constant.ts b/server/src/domain/media/media.constant.ts deleted file mode 100644 index 3a8ee414b..000000000 --- a/server/src/domain/media/media.constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const FACE_THUMBNAIL_SIZE = 250; diff --git a/server/src/domain/smart-info/smart-info.constant.ts b/server/src/domain/smart-info/smart-info.constant.ts deleted file mode 100644 index 66c31b985..000000000 --- a/server/src/domain/smart-info/smart-info.constant.ts +++ /dev/null @@ -1,129 +0,0 @@ -export type ModelInfo = { - dimSize: number; -}; - -export const CLIP_MODEL_INFO: Record = { - RN50__openai: { - dimSize: 1024, - }, - RN50__yfcc15m: { - dimSize: 1024, - }, - RN50__cc12m: { - dimSize: 1024, - }, - RN101__openai: { - dimSize: 512, - }, - RN101__yfcc15m: { - dimSize: 512, - }, - RN50x4__openai: { - dimSize: 640, - }, - RN50x16__openai: { - dimSize: 768, - }, - RN50x64__openai: { - dimSize: 1024, - }, - 'ViT-B-32__openai': { - dimSize: 512, - }, - 'ViT-B-32__laion2b_e16': { - dimSize: 512, - }, - 'ViT-B-32__laion400m_e31': { - dimSize: 512, - }, - 'ViT-B-32__laion400m_e32': { - dimSize: 512, - }, - 'ViT-B-32__laion2b-s34b-b79k': { - dimSize: 512, - }, - 'ViT-B-16__openai': { - dimSize: 512, - }, - 'ViT-B-16__laion400m_e31': { - dimSize: 512, - }, - 'ViT-B-16__laion400m_e32': { - dimSize: 512, - }, - 'ViT-B-16-plus-240__laion400m_e31': { - dimSize: 640, - }, - 'ViT-B-16-plus-240__laion400m_e32': { - dimSize: 640, - }, - 'ViT-L-14__openai': { - dimSize: 768, - }, - 'ViT-L-14__laion400m_e31': { - dimSize: 768, - }, - 'ViT-L-14__laion400m_e32': { - dimSize: 768, - }, - 'ViT-L-14__laion2b-s32b-b82k': { - dimSize: 768, - }, - 'ViT-L-14-336__openai': { - dimSize: 768, - }, - 'ViT-L-14-quickgelu__dfn2b': { - dimSize: 768, - }, - 'ViT-H-14__laion2b-s32b-b79k': { - dimSize: 1024, - }, - 'ViT-H-14-quickgelu__dfn5b': { - dimSize: 1024, - }, - 'ViT-H-14-378-quickgelu__dfn5b': { - dimSize: 1024, - }, - 'ViT-g-14__laion2b-s12b-b42k': { - dimSize: 1024, - }, - 'LABSE-Vit-L-14': { - dimSize: 768, - }, - 'XLM-Roberta-Large-Vit-B-32': { - dimSize: 512, - }, - 'XLM-Roberta-Large-Vit-B-16Plus': { - dimSize: 640, - }, - 'XLM-Roberta-Large-Vit-L-14': { - dimSize: 768, - }, - 'XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k': { - dimSize: 1024, - }, - 'nllb-clip-base-siglip__v1': { - dimSize: 768, - }, - 'nllb-clip-large-siglip__v1': { - dimSize: 1152, - }, -}; - -export function cleanModelName(modelName: string): string { - const token = modelName.split('/').at(-1); - if (!token) { - throw new Error(`Invalid model name: ${modelName}`); - } - - return token.replaceAll(':', '_'); -} - -export function getCLIPModelInfo(modelName: string): ModelInfo { - const modelInfo = CLIP_MODEL_INFO[cleanModelName(modelName)]; - if (!modelInfo) { - throw new Error(`Unknown CLIP model: ${modelName}`); - } - - return modelInfo; -} diff --git a/server/src/domain/system-config/system-config.constants.ts b/server/src/domain/system-config/system-config.constants.ts deleted file mode 100644 index 0290472aa..000000000 --- a/server/src/domain/system-config/system-config.constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const supportedYearTokens = ['y', 'yy']; -export const supportedMonthTokens = ['M', 'MM', 'MMM', 'MMMM']; -export const supportedWeekTokens = ['W', 'WW']; -export const supportedDayTokens = ['d', 'dd']; -export const supportedHourTokens = ['h', 'hh', 'H', 'HH']; -export const supportedMinuteTokens = ['m', 'mm']; -export const supportedSecondTokens = ['s', 'ss', 'SSS']; -export const supportedPresetTokens = [ - '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', - '{{y}}/{{MM}}-{{dd}}/{{filename}}', - '{{y}}/{{MMMM}}-{{dd}}/{{filename}}', - '{{y}}/{{MM}}/{{filename}}', - '{{y}}/{{MMM}}/{{filename}}', - '{{y}}/{{MMMM}}/{{filename}}', - '{{y}}/{{MM}}/{{dd}}/{{filename}}', - '{{y}}/{{MMMM}}/{{dd}}/{{filename}}', - '{{y}}/{{y}}-{{MM}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', - '{{y}}-{{MM}}-{{dd}}/{{filename}}', - '{{y}}-{{MMM}}-{{dd}}/{{filename}}', - '{{y}}-{{MMMM}}-{{dd}}/{{filename}}', - '{{y}}/{{y}}-{{MM}}/{{filename}}', - '{{y}}/{{y}}-{{WW}}/{{filename}}', - '{{y}}/{{y}}-{{MM}}-{{dd}}/{{assetId}}', - '{{y}}/{{y}}-{{MM}}/{{assetId}}', - '{{y}}/{{y}}-{{WW}}/{{assetId}}', - '{{album}}/{{filename}}', -]; diff --git a/server/src/dtos/job.dto.ts b/server/src/dtos/job.dto.ts index fd463a9b0..ad2ffec11 100644 --- a/server/src/dtos/job.dto.ts +++ b/server/src/dtos/job.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsNotEmpty } from 'class-validator'; -import { JobCommand, QueueName } from 'src/domain/job/job.constants'; +import { JobCommand, QueueName } from 'src/interfaces/job.repository'; import { ValidateBoolean } from 'src/validation'; export class JobIdParamDto { diff --git a/server/src/dtos/server-info.dto.ts b/server/src/dtos/server-info.dto.ts index 33fe83044..cce0de59a 100644 --- a/server/src/dtos/server-info.dto.ts +++ b/server/src/dtos/server-info.dto.ts @@ -1,8 +1,8 @@ import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger'; import type { DateTime } from 'luxon'; import { FeatureFlags } from 'src/cores/system-config.core'; -import { IVersion, VersionType } from 'src/domain/domain.constant'; import { SystemConfigThemeDto } from 'src/dtos/system-config-theme.dto'; +import { IVersion, VersionType } from 'src/utils/version'; export class ServerPingResponse { @ApiResponseProperty({ type: String, example: 'pong' }) diff --git a/server/src/dtos/system-config-job.dto.ts b/server/src/dtos/system-config-job.dto.ts index 2769da327..86f4dad1d 100644 --- a/server/src/dtos/system-config-job.dto.ts +++ b/server/src/dtos/system-config-job.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator'; -import { ConcurrentQueueName, QueueName } from 'src/domain/job/job.constants'; +import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.repository'; export class JobSettingsDto { @IsInt() diff --git a/server/src/entities/system-config.entity.ts b/server/src/entities/system-config.entity.ts index cf7474417..23eae493b 100644 --- a/server/src/entities/system-config.entity.ts +++ b/server/src/entities/system-config.entity.ts @@ -1,4 +1,4 @@ -import { ConcurrentQueueName } from 'src/domain/job/job.constants'; +import { ConcurrentQueueName } from 'src/interfaces/job.repository'; import { Column, Entity, PrimaryColumn } from 'typeorm'; @Entity('system_config') diff --git a/server/src/immich/api-v1/asset/asset-repository.ts b/server/src/immich/api-v1/asset/asset-repository.ts index e7b870239..af01d4ce0 100644 --- a/server/src/immich/api-v1/asset/asset-repository.ts +++ b/server/src/immich/api-v1/asset/asset-repository.ts @@ -7,7 +7,7 @@ import { CheckExistingAssetsDto } from 'src/immich/api-v1/asset/dto/check-existi import { SearchPropertiesDto } from 'src/immich/api-v1/asset/dto/search-properties.dto'; import { CuratedLocationsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-locations-response.dto'; import { CuratedObjectsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-objects-response.dto'; -import { OptionalBetween } from 'src/infra/infra.utils'; +import { OptionalBetween } from 'src/utils/database'; import { In } from 'typeorm/find-options/operator/In.js'; import { Repository } from 'typeorm/repository/Repository.js'; export interface AssetCheck { diff --git a/server/src/immich/api-v1/asset/asset.service.spec.ts b/server/src/immich/api-v1/asset/asset.service.spec.ts index d10590475..0b465e42c 100644 --- a/server/src/immich/api-v1/asset/asset.service.spec.ts +++ b/server/src/immich/api-v1/asset/asset.service.spec.ts @@ -1,5 +1,4 @@ import { when } from 'jest-when'; -import { JobName } from 'src/domain/job/job.constants'; import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { IAssetRepositoryV1 } from 'src/immich/api-v1/asset/asset-repository'; @@ -7,7 +6,7 @@ import { AssetService } from 'src/immich/api-v1/asset/asset.service'; import { CreateAssetDto } from 'src/immich/api-v1/asset/dto/create-asset.dto'; import { AssetRejectReason, AssetUploadAction } from 'src/immich/api-v1/asset/response-dto/asset-check-response.dto'; import { IAssetRepository } from 'src/interfaces/asset.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index 023017130..61ad72c56 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -6,8 +6,6 @@ import { NotFoundException, } from '@nestjs/common'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { mimeTypes } from 'src/domain/domain.constant'; -import { JobName } from 'src/domain/job/job.constants'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity'; @@ -28,15 +26,16 @@ import { AssetFileUploadResponseDto } from 'src/immich/api-v1/asset/response-dto import { CheckExistingAssetsResponseDto } from 'src/immich/api-v1/asset/response-dto/check-existing-assets-response.dto'; import { CuratedLocationsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-locations-response.dto'; import { CuratedObjectsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-objects-response.dto'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; import { UploadFile } from 'src/services/asset.service'; -import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils'; +import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils/file'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; import { QueryFailedError } from 'typeorm'; @Injectable() diff --git a/server/src/immich/app.utils.ts b/server/src/immich/app.utils.ts index f0e4ba661..46eeccadb 100644 --- a/server/src/immich/app.utils.ts +++ b/server/src/immich/app.utils.ts @@ -13,12 +13,12 @@ import { writeFileSync } from 'node:fs'; import { access, constants } from 'node:fs/promises'; import path, { isAbsolute } from 'node:path'; import { promisify } from 'node:util'; -import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant'; -import { serverVersion } from 'src/domain/domain.constant'; -import { ImmichLogger } from 'src/infra/logger'; +import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME, serverVersion } from 'src/constants'; import { ImmichReadStream } from 'src/interfaces/storage.repository'; import { Metadata } from 'src/middleware/auth.guard'; -import { CacheControl, ImmichFileResponse, isConnectionAborted } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; +import { ImmichLogger } from 'src/utils/logger'; +import { isConnectionAborted } from 'src/utils/misc'; type SendFile = Parameters; type SendFileOptions = SendFile[1]; diff --git a/server/src/infra/sql-generator/sql.logger.ts b/server/src/infra/sql-generator/sql.logger.ts deleted file mode 100644 index 6f3c298c0..000000000 --- a/server/src/infra/sql-generator/sql.logger.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { format } from 'sql-formatter'; -import { Logger } from 'typeorm'; - -export class SqlLogger implements Logger { - queries: string[] = []; - errors: Array<{ error: string | Error; query: string }> = []; - - clear() { - this.queries = []; - this.errors = []; - } - - logQuery(query: string) { - this.queries.push(format(query, { language: 'postgresql' })); - } - - logQueryError(error: string | Error, query: string) { - this.errors.push({ error, query }); - } - - logQuerySlow() {} - logSchemaBuild() {} - logMigration() {} - log() {} -} diff --git a/server/src/interfaces/asset.repository.ts b/server/src/interfaces/asset.repository.ts index 9bca4a6df..379357b62 100644 --- a/server/src/interfaces/asset.repository.ts +++ b/server/src/interfaces/asset.repository.ts @@ -4,7 +4,7 @@ import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { ReverseGeocodeResult } from 'src/interfaces/metadata.repository'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.repository'; -import { Paginated, PaginationOptions } from 'src/utils'; +import { Paginated, PaginationOptions } from 'src/utils/pagination'; import { FindOptionsRelations, FindOptionsSelect } from 'typeorm'; export type AssetStats = Record; diff --git a/server/src/interfaces/database.repository.ts b/server/src/interfaces/database.repository.ts index e87775c3e..42342eccc 100644 --- a/server/src/interfaces/database.repository.ts +++ b/server/src/interfaces/database.repository.ts @@ -1,4 +1,4 @@ -import { Version } from 'src/domain/domain.constant'; +import { Version } from 'src/utils/version'; export enum DatabaseExtension { CUBE = 'cube', diff --git a/server/src/interfaces/job.repository.ts b/server/src/interfaces/job.repository.ts index 023f6a8e9..6f07fc752 100644 --- a/server/src/interfaces/job.repository.ts +++ b/server/src/interfaces/job.repository.ts @@ -1,14 +1,139 @@ -import { JobName, QueueName } from 'src/domain/job/job.constants'; -import { - IAssetDeletionJob, - IBaseJob, - IDeferrableJob, - IDeleteFilesJob, - IEntityJob, - ILibraryFileJob, - ILibraryRefreshJob, - ISidecarWriteJob, -} from 'src/domain/job/job.interface'; +export enum QueueName { + THUMBNAIL_GENERATION = 'thumbnailGeneration', + METADATA_EXTRACTION = 'metadataExtraction', + VIDEO_CONVERSION = 'videoConversion', + FACE_DETECTION = 'faceDetection', + FACIAL_RECOGNITION = 'facialRecognition', + SMART_SEARCH = 'smartSearch', + BACKGROUND_TASK = 'backgroundTask', + STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration', + MIGRATION = 'migration', + SEARCH = 'search', + SIDECAR = 'sidecar', + LIBRARY = 'library', +} + +export type ConcurrentQueueName = Exclude< + QueueName, + QueueName.STORAGE_TEMPLATE_MIGRATION | QueueName.FACIAL_RECOGNITION +>; + +export enum JobCommand { + START = 'start', + PAUSE = 'pause', + RESUME = 'resume', + EMPTY = 'empty', + CLEAR_FAILED = 'clear-failed', +} + +export enum JobName { + // conversion + QUEUE_VIDEO_CONVERSION = 'queue-video-conversion', + VIDEO_CONVERSION = 'video-conversion', + + // thumbnails + QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails', + GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail', + GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail', + GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail', + GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail', + + // metadata + QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction', + METADATA_EXTRACTION = 'metadata-extraction', + LINK_LIVE_PHOTOS = 'link-live-photos', + + // user + USER_DELETION = 'user-deletion', + USER_DELETE_CHECK = 'user-delete-check', + USER_SYNC_USAGE = 'user-sync-usage', + + // asset + ASSET_DELETION = 'asset-deletion', + ASSET_DELETION_CHECK = 'asset-deletion-check', + + // storage template + STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration', + STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single', + + // migration + QUEUE_MIGRATION = 'queue-migration', + MIGRATE_ASSET = 'migrate-asset', + MIGRATE_PERSON = 'migrate-person', + + // facial recognition + PERSON_CLEANUP = 'person-cleanup', + QUEUE_FACE_DETECTION = 'queue-face-detection', + FACE_DETECTION = 'face-detection', + QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition', + FACIAL_RECOGNITION = 'facial-recognition', + + // library management + LIBRARY_SCAN = 'library-refresh', + LIBRARY_SCAN_ASSET = 'library-refresh-asset', + LIBRARY_REMOVE_OFFLINE = 'library-remove-offline', + LIBRARY_DELETE = 'library-delete', + LIBRARY_QUEUE_SCAN_ALL = 'library-queue-all-refresh', + LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup', + + // cleanup + DELETE_FILES = 'delete-files', + CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs', + + // smart search + QUEUE_SMART_SEARCH = 'queue-smart-search', + SMART_SEARCH = 'smart-search', + + // XMP sidecars + QUEUE_SIDECAR = 'queue-sidecar', + SIDECAR_DISCOVERY = 'sidecar-discovery', + SIDECAR_SYNC = 'sidecar-sync', + SIDECAR_WRITE = 'sidecar-write', +} + +export const JOBS_ASSET_PAGINATION_SIZE = 1000; + +export interface IBaseJob { + force?: boolean; +} + +export interface IEntityJob extends IBaseJob { + id: string; + source?: 'upload' | 'sidecar-write'; +} + +export interface IAssetDeletionJob extends IEntityJob { + fromExternal?: boolean; +} + +export interface ILibraryFileJob extends IEntityJob { + ownerId: string; + assetPath: string; +} + +export interface ILibraryRefreshJob extends IEntityJob { + refreshModifiedFiles: boolean; + refreshAllFiles: boolean; +} + +export interface IBulkEntityJob extends IBaseJob { + ids: string[]; +} + +export interface IDeleteFilesJob extends IBaseJob { + files: Array; +} + +export interface ISidecarWriteJob extends IEntityJob { + description?: string; + dateTimeOriginal?: string; + latitude?: number; + longitude?: number; +} + +export interface IDeferrableJob extends IEntityJob { + deferred?: boolean; +} export interface JobCounts { active: number; diff --git a/server/src/interfaces/person.repository.ts b/server/src/interfaces/person.repository.ts index cba11fbb9..382bbda22 100644 --- a/server/src/interfaces/person.repository.ts +++ b/server/src/interfaces/person.repository.ts @@ -1,7 +1,7 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { Paginated, PaginationOptions } from 'src/utils'; +import { Paginated, PaginationOptions } from 'src/utils/pagination'; import { FindManyOptions, FindOptionsRelations, FindOptionsSelect } from 'typeorm'; export const IPersonRepository = 'IPersonRepository'; diff --git a/server/src/interfaces/search.repository.ts b/server/src/interfaces/search.repository.ts index 29eaa32b7..a4be05549 100644 --- a/server/src/interfaces/search.repository.ts +++ b/server/src/interfaces/search.repository.ts @@ -2,7 +2,7 @@ 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'; +import { Paginated } from 'src/utils/pagination'; export const ISearchRepository = 'ISearchRepository'; diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 48befd9a2..eaa47d013 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -9,10 +9,10 @@ import { import { Reflector } from '@nestjs/core'; import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; -import { IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant'; +import { IMMICH_API_KEY_NAME } from 'src/constants'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichLogger } from 'src/infra/logger'; import { AuthService, LoginDetails } from 'src/services/auth.service'; +import { ImmichLogger } from 'src/utils/logger'; import { UAParser } from 'ua-parser-js'; export enum Metadata { diff --git a/server/src/middleware/error.interceptor.ts b/server/src/middleware/error.interceptor.ts index 70c129922..d354c1107 100644 --- a/server/src/middleware/error.interceptor.ts +++ b/server/src/middleware/error.interceptor.ts @@ -8,8 +8,8 @@ import { } from '@nestjs/common'; import { Observable, catchError, throwError } from 'rxjs'; import { routeToErrorMessage } from 'src/immich/app.utils'; -import { ImmichLogger } from 'src/infra/logger'; -import { isConnectionAborted } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { isConnectionAborted } from 'src/utils/misc'; @Injectable() export class ErrorInterceptor implements NestInterceptor { diff --git a/server/src/middleware/file-upload.interceptor.ts b/server/src/middleware/file-upload.interceptor.ts index f1d5c5c34..53acbefa8 100644 --- a/server/src/middleware/file-upload.interceptor.ts +++ b/server/src/middleware/file-upload.interceptor.ts @@ -7,9 +7,9 @@ import multer, { StorageEngine, diskStorage } from 'multer'; import { createHash, randomUUID } from 'node:crypto'; import { Observable } from 'rxjs'; import { UploadFieldName } from 'src/dtos/asset.dto'; -import { ImmichLogger } from 'src/infra/logger'; import { AuthRequest } from 'src/middleware/auth.guard'; import { AssetService, UploadFile } from 'src/services/asset.service'; +import { ImmichLogger } from 'src/utils/logger'; export enum Route { ASSET = 'asset', diff --git a/server/src/infra/websocket.adapter.ts b/server/src/middleware/websocket.adapter.ts similarity index 100% rename from server/src/infra/websocket.adapter.ts rename to server/src/middleware/websocket.adapter.ts diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts index 46d5320ce..75c85e3e0 100644 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ b/server/src/migrations/1700713871511-UsePgVectors.ts @@ -1,5 +1,5 @@ -import { getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant'; -import { vectorExt } from 'src/infra/database.config'; +import { vectorExt } from 'src/database.config'; +import { getCLIPModelInfo } from 'src/utils/misc'; import { MigrationInterface, QueryRunner } from 'typeorm'; export class UsePgVectors1700713871511 implements MigrationInterface { diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts index b045ba62b..6ca4952f1 100644 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts @@ -1,4 +1,4 @@ -import { vectorExt } from 'src/infra/database.config'; +import { vectorExt } from 'src/database.config'; import { DatabaseExtension } from 'src/interfaces/database.repository'; import { MigrationInterface, QueryRunner } from 'typeorm'; diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts index e77ce3b0b..cc68d689c 100644 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts @@ -1,4 +1,4 @@ -import { vectorExt } from 'src/infra/database.config'; +import { vectorExt } from 'src/database.config'; import { DatabaseExtension } from 'src/interfaces/database.repository'; import { MigrationInterface, QueryRunner } from 'typeorm'; diff --git a/server/src/infra/sql/access.repository.sql b/server/src/queries/access.repository.sql similarity index 100% rename from server/src/infra/sql/access.repository.sql rename to server/src/queries/access.repository.sql diff --git a/server/src/infra/sql/album.repository.sql b/server/src/queries/album.repository.sql similarity index 100% rename from server/src/infra/sql/album.repository.sql rename to server/src/queries/album.repository.sql diff --git a/server/src/infra/sql/api.key.repository.sql b/server/src/queries/api.key.repository.sql similarity index 100% rename from server/src/infra/sql/api.key.repository.sql rename to server/src/queries/api.key.repository.sql diff --git a/server/src/infra/sql/asset.repository.sql b/server/src/queries/asset.repository.sql similarity index 100% rename from server/src/infra/sql/asset.repository.sql rename to server/src/queries/asset.repository.sql diff --git a/server/src/infra/sql/audit.repository.sql b/server/src/queries/audit.repository.sql similarity index 100% rename from server/src/infra/sql/audit.repository.sql rename to server/src/queries/audit.repository.sql diff --git a/server/src/infra/sql/library.repository.sql b/server/src/queries/library.repository.sql similarity index 100% rename from server/src/infra/sql/library.repository.sql rename to server/src/queries/library.repository.sql diff --git a/server/src/infra/sql/move.repository.sql b/server/src/queries/move.repository.sql similarity index 100% rename from server/src/infra/sql/move.repository.sql rename to server/src/queries/move.repository.sql diff --git a/server/src/infra/sql/partner.repository.sql b/server/src/queries/partner.repository.sql similarity index 100% rename from server/src/infra/sql/partner.repository.sql rename to server/src/queries/partner.repository.sql diff --git a/server/src/infra/sql/person.repository.sql b/server/src/queries/person.repository.sql similarity index 100% rename from server/src/infra/sql/person.repository.sql rename to server/src/queries/person.repository.sql diff --git a/server/src/infra/sql/search.repository.sql b/server/src/queries/search.repository.sql similarity index 100% rename from server/src/infra/sql/search.repository.sql rename to server/src/queries/search.repository.sql diff --git a/server/src/infra/sql/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql similarity index 100% rename from server/src/infra/sql/shared.link.repository.sql rename to server/src/queries/shared.link.repository.sql diff --git a/server/src/infra/sql/system.config.repository.sql b/server/src/queries/system.config.repository.sql similarity index 100% rename from server/src/infra/sql/system.config.repository.sql rename to server/src/queries/system.config.repository.sql diff --git a/server/src/infra/sql/system.metadata.repository.sql b/server/src/queries/system.metadata.repository.sql similarity index 100% rename from server/src/infra/sql/system.metadata.repository.sql rename to server/src/queries/system.metadata.repository.sql diff --git a/server/src/infra/sql/tag.repository.sql b/server/src/queries/tag.repository.sql similarity index 100% rename from server/src/infra/sql/tag.repository.sql rename to server/src/queries/tag.repository.sql diff --git a/server/src/infra/sql/user.repository.sql b/server/src/queries/user.repository.sql similarity index 100% rename from server/src/infra/sql/user.repository.sql rename to server/src/queries/user.repository.sql diff --git a/server/src/infra/sql/user.token.repository.sql b/server/src/queries/user.token.repository.sql similarity index 100% rename from server/src/infra/sql/user.token.repository.sql rename to server/src/queries/user.token.repository.sql diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 418cf542f..fc067f2c2 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -9,8 +9,8 @@ import { PartnerEntity } from 'src/entities/partner.entity'; import { PersonEntity } from 'src/entities/person.entity'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; import { UserTokenEntity } from 'src/entities/user-token.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IAccessRepository } from 'src/interfaces/access.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Brackets, In, Repository } from 'typeorm'; type IActivityAccess = IAccessRepository['activity']; diff --git a/server/src/repositories/activity.repository.ts b/server/src/repositories/activity.repository.ts index bec4a4f17..6d4ef244e 100644 --- a/server/src/repositories/activity.repository.ts +++ b/server/src/repositories/activity.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { ActivityEntity } from 'src/entities/activity.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IActivityRepository } from 'src/interfaces/activity.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Repository } from 'typeorm'; export interface ActivitySearch { diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index 4646ef3d9..c8ad98325 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -1,11 +1,10 @@ import { Injectable } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import _ from 'lodash'; +import { dataSource } from 'src/database.config'; import { Chunked, ChunkedArray, DATABASE_PARAMETER_CHUNK_SIZE, DummyValue, GenerateSql } from 'src/decorators'; import { AlbumEntity } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; -import { dataSource } from 'src/infra/database.config'; -import { Instrumentation } from 'src/infra/instrumentation'; import { AlbumAsset, AlbumAssetCount, @@ -13,7 +12,8 @@ import { AlbumInfoOptions, IAlbumRepository, } from 'src/interfaces/album.repository'; -import { setUnion } from 'src/utils'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { setUnion } from 'src/utils/set'; import { DataSource, FindOptionsOrder, FindOptionsRelations, In, IsNull, Not, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/api-key.repository.ts b/server/src/repositories/api-key.repository.ts index 14a7db9c9..4ee79f3b4 100644 --- a/server/src/repositories/api-key.repository.ts +++ b/server/src/repositories/api-key.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { APIKeyEntity } from 'src/entities/api-key.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/asset-stack.repository.ts b/server/src/repositories/asset-stack.repository.ts index 9859c5fad..404908ee0 100644 --- a/server/src/repositories/asset-stack.repository.ts +++ b/server/src/repositories/asset-stack.repository.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AssetStackEntity } from 'src/entities/asset-stack.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 71bef17ef..0fb46a120 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -8,8 +8,6 @@ import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity'; -import { OptionalBetween, paginate, paginatedBuilder, searchAssetBuilder } from 'src/infra/infra.utils'; -import { Instrumentation } from 'src/infra/instrumentation'; import { AssetBuilderOptions, AssetCreate, @@ -32,7 +30,9 @@ import { WithoutProperty, } from 'src/interfaces/asset.repository'; import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.repository'; -import { Paginated, PaginationMode, PaginationOptions } from 'src/utils'; +import { OptionalBetween, searchAssetBuilder } from 'src/utils/database'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { Paginated, PaginationMode, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination'; import { Brackets, FindOptionsRelations, diff --git a/server/src/repositories/audit.repository.ts b/server/src/repositories/audit.repository.ts index 1d892dab2..ef8081969 100644 --- a/server/src/repositories/audit.repository.ts +++ b/server/src/repositories/audit.repository.ts @@ -1,7 +1,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { AuditEntity } from 'src/entities/audit.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { AuditSearch, IAuditRepository } from 'src/interfaces/audit.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { LessThan, MoreThan, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/communication.repository.ts b/server/src/repositories/communication.repository.ts index 549975c74..531b63f51 100644 --- a/server/src/repositories/communication.repository.ts +++ b/server/src/repositories/communication.repository.ts @@ -7,8 +7,6 @@ import { WebSocketServer, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { ClientEvent, ICommunicationRepository, @@ -18,6 +16,8 @@ import { ServerEvent, } from 'src/interfaces/communication.repository'; import { AuthService } from 'src/services/auth.service'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; @Instrumentation() @WebSocketGateway({ diff --git a/server/src/repositories/crypto.repository.ts b/server/src/repositories/crypto.repository.ts index 121943af3..f7cfa43ff 100644 --- a/server/src/repositories/crypto.repository.ts +++ b/server/src/repositories/crypto.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { compareSync, hash } from 'bcrypt'; import { createHash, randomBytes, randomUUID } from 'node:crypto'; import { createReadStream } from 'node:fs'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; @Instrumentation() @Injectable() diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 60586edc5..4e4632741 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -1,10 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import AsyncLock from 'async-lock'; -import { Version, VersionType } from 'src/domain/domain.constant'; -import { vectorExt } from 'src/infra/database.config'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; +import { vectorExt } from 'src/database.config'; import { DatabaseExtension, DatabaseLock, @@ -14,6 +11,9 @@ import { VectorUpdateResult, extName, } from 'src/interfaces/database.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; +import { Version, VersionType } from 'src/utils/version'; import { isValidInteger } from 'src/validation'; import { DataSource, EntityManager, QueryRunner } from 'typeorm'; diff --git a/server/src/repositories/filesystem.provider.ts b/server/src/repositories/filesystem.provider.ts index 1e65f9664..3722893d8 100644 --- a/server/src/repositories/filesystem.provider.ts +++ b/server/src/repositories/filesystem.provider.ts @@ -4,10 +4,7 @@ import { glob, globStream } from 'fast-glob'; import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; -import { mimeTypes } from 'src/domain/domain.constant'; import { CrawlOptionsDto } from 'src/dtos/library.dto'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { DiskUsage, IStorageRepository, @@ -16,6 +13,9 @@ import { StorageEventType, WatchEvents, } from 'src/interfaces/storage.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; @Instrumentation() export class FilesystemProvider implements IStorageRepository { diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index a7637e0e6..2d07d7748 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -6,10 +6,78 @@ import { Job, JobsOptions, Processor, Queue, Worker, WorkerOptions } from 'bullm import { CronJob, CronTime } from 'cron'; import { setTimeout } from 'node:timers/promises'; import { bullConfig } from 'src/config'; -import { JOBS_TO_QUEUE, JobName, QueueName } from 'src/domain/job/job.constants'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; -import { IJobRepository, JobCounts, JobItem, QueueCleanType, QueueStatus } from 'src/interfaces/job.repository'; +import { + IJobRepository, + JobCounts, + JobItem, + JobName, + QueueCleanType, + QueueName, + QueueStatus, +} from 'src/interfaces/job.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; + +export const JOBS_TO_QUEUE: Record = { + // misc + [JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK, + [JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK, + [JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK, + [JobName.USER_DELETION]: QueueName.BACKGROUND_TASK, + [JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK, + [JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK, + [JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK, + [JobName.USER_SYNC_USAGE]: QueueName.BACKGROUND_TASK, + + // conversion + [JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION, + [JobName.VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION, + + // thumbnails + [JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION, + [JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, + [JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, + [JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, + [JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION, + + // metadata + [JobName.QUEUE_METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION, + [JobName.METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION, + [JobName.LINK_LIVE_PHOTOS]: QueueName.METADATA_EXTRACTION, + + // storage template + [JobName.STORAGE_TEMPLATE_MIGRATION]: QueueName.STORAGE_TEMPLATE_MIGRATION, + [JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: QueueName.STORAGE_TEMPLATE_MIGRATION, + + // migration + [JobName.QUEUE_MIGRATION]: QueueName.MIGRATION, + [JobName.MIGRATE_ASSET]: QueueName.MIGRATION, + [JobName.MIGRATE_PERSON]: QueueName.MIGRATION, + + // facial recognition + [JobName.QUEUE_FACE_DETECTION]: QueueName.FACE_DETECTION, + [JobName.FACE_DETECTION]: QueueName.FACE_DETECTION, + [JobName.QUEUE_FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION, + [JobName.FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION, + + // smart search + [JobName.QUEUE_SMART_SEARCH]: QueueName.SMART_SEARCH, + [JobName.SMART_SEARCH]: QueueName.SMART_SEARCH, + + // XMP sidecars + [JobName.QUEUE_SIDECAR]: QueueName.SIDECAR, + [JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR, + [JobName.SIDECAR_SYNC]: QueueName.SIDECAR, + [JobName.SIDECAR_WRITE]: QueueName.SIDECAR, + + // Library management + [JobName.LIBRARY_SCAN_ASSET]: QueueName.LIBRARY, + [JobName.LIBRARY_SCAN]: QueueName.LIBRARY, + [JobName.LIBRARY_DELETE]: QueueName.LIBRARY, + [JobName.LIBRARY_REMOVE_OFFLINE]: QueueName.LIBRARY, + [JobName.LIBRARY_QUEUE_SCAN_ALL]: QueueName.LIBRARY, + [JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY, +}; @Instrumentation() @Injectable() diff --git a/server/src/repositories/library.repository.ts b/server/src/repositories/library.repository.ts index 152f544ce..ddf63f228 100644 --- a/server/src/repositories/library.repository.ts +++ b/server/src/repositories/library.repository.ts @@ -3,8 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { LibraryStatsResponseDto } from 'src/dtos/library.dto'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ILibraryRepository } from 'src/interfaces/library.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Not } from 'typeorm'; import { Repository } from 'typeorm/repository/Repository.js'; diff --git a/server/src/repositories/machine-learning.repository.ts b/server/src/repositories/machine-learning.repository.ts index 34f0cdbfc..75d887943 100644 --- a/server/src/repositories/machine-learning.repository.ts +++ b/server/src/repositories/machine-learning.repository.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { readFile } from 'node:fs/promises'; import { CLIPConfig, ModelConfig, RecognitionConfig } from 'src/dtos/model-config.dto'; -import { Instrumentation } from 'src/infra/instrumentation'; import { CLIPMode, DetectFaceResult, @@ -10,6 +9,7 @@ import { TextModelInput, VisionModelInput, } from 'src/interfaces/machine-learning.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; const errorPrefix = 'Machine learning request'; diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index d0f4332b4..890657e3b 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -4,8 +4,6 @@ import { Writable } from 'node:stream'; import { promisify } from 'node:util'; import sharp from 'sharp'; import { Colorspace } from 'src/entities/system-config.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { CropOptions, IMediaRepository, @@ -13,7 +11,9 @@ import { TranscodeOptions, VideoInfo, } from 'src/interfaces/media.repository'; -import { handlePromiseError } from 'src/utils'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; +import { handlePromiseError } from 'src/utils/misc'; const probe = promisify(ffmpeg.ffprobe); sharp.concurrency(0); diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index 5da664f14..1b4dd2b3c 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -6,21 +6,15 @@ import { getName } from 'i18n-iso-countries'; import { createReadStream, existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import readLine from 'node:readline'; +import { citiesFile, geodataAdmin1Path, geodataAdmin2Path, geodataCities500Path, geodataDatePath } from 'src/constants'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { - citiesFile, - geodataAdmin1Path, - geodataAdmin2Path, - geodataCities500Path, - geodataDatePath, -} from 'src/domain/domain.constant'; import { ExifEntity } from 'src/entities/exif.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { GeoPoint, IMetadataRepository, ImmichTags, ReverseGeocodeResult } from 'src/interfaces/metadata.repository'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; import { DataSource, QueryRunner, Repository } from 'typeorm'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; diff --git a/server/src/repositories/move.repository.ts b/server/src/repositories/move.repository.ts index e567bcca3..5a17d72a5 100644 --- a/server/src/repositories/move.repository.ts +++ b/server/src/repositories/move.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { MoveEntity, PathType } from 'src/entities/move.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IMoveRepository, MoveCreate } from 'src/interfaces/move.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/partner.repository.ts b/server/src/repositories/partner.repository.ts index 4c4bbc0ed..a07241b53 100644 --- a/server/src/repositories/partner.repository.ts +++ b/server/src/repositories/partner.repository.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { PartnerEntity } from 'src/entities/partner.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { DeepPartial, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 6a0008c3d..805c91d92 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -4,8 +4,6 @@ import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { asVector, paginate } from 'src/infra/infra.utils'; -import { Instrumentation } from 'src/infra/instrumentation'; import { AssetFaceId, IPersonRepository, @@ -15,7 +13,9 @@ import { PersonStatistics, UpdateFacesData, } from 'src/interfaces/person.repository'; -import { Paginated, PaginationOptions } from 'src/utils'; +import { asVector } from 'src/utils/database'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { Paginated, PaginationOptions, paginate } from 'src/utils/pagination'; import { FindManyOptions, FindOptionsRelations, FindOptionsSelect, In, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index aefb3c933..8c48337ce 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -1,16 +1,12 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { vectorExt } from 'src/database.config'; import { DummyValue, GenerateSql } from 'src/decorators'; -import { getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant'; 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 { SmartSearchEntity } from 'src/entities/smart-search.entity'; -import { vectorExt } from 'src/infra/database.config'; -import { asVector, paginatedBuilder, searchAssetBuilder } from 'src/infra/infra.utils'; -import { Instrumentation } from 'src/infra/instrumentation'; -import { ImmichLogger } from 'src/infra/logger'; import { DatabaseExtension } from 'src/interfaces/database.repository'; import { AssetSearchOptions, @@ -21,7 +17,11 @@ import { SearchPaginationOptions, SmartSearchOptions, } from 'src/interfaces/search.repository'; -import { Paginated, PaginationMode, PaginationResult } from 'src/utils'; +import { asVector, searchAssetBuilder } from 'src/utils/database'; +import { Instrumentation } from 'src/utils/instrumentation'; +import { ImmichLogger } from 'src/utils/logger'; +import { getCLIPModelInfo } from 'src/utils/misc'; +import { Paginated, PaginationMode, PaginationResult, paginatedBuilder } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; import { Repository, SelectQueryBuilder } from 'typeorm'; diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index fcef0a396..3c25bc65b 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { Instrumentation } from 'src/infra/instrumentation'; import { GitHubRelease, IServerInfoRepository } from 'src/interfaces/server-info.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; @Instrumentation() @Injectable() diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index 968b6dd73..0e343da5e 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SharedLinkEntity } from 'src/entities/shared-link.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/system-config.repository.ts b/server/src/repositories/system-config.repository.ts index af6296ce0..ac300fb5d 100644 --- a/server/src/repositories/system-config.repository.ts +++ b/server/src/repositories/system-config.repository.ts @@ -2,8 +2,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { readFile } from 'node:fs/promises'; import { Chunked, DummyValue, GenerateSql } from 'src/decorators'; import { SystemConfigEntity } from 'src/entities/system-config.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { In, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/system-metadata.repository.ts b/server/src/repositories/system-metadata.repository.ts index 978dba9bd..60bf47965 100644 --- a/server/src/repositories/system-metadata.repository.ts +++ b/server/src/repositories/system-metadata.repository.ts @@ -1,7 +1,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { SystemMetadata, SystemMetadataEntity } from 'src/entities/system-metadata.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/tag.repository.ts b/server/src/repositories/tag.repository.ts index a2ae74878..5e2b2bb6d 100644 --- a/server/src/repositories/tag.repository.ts +++ b/server/src/repositories/tag.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AssetEntity } from 'src/entities/asset.entity'; import { TagEntity } from 'src/entities/tag.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { ITagRepository } from 'src/interfaces/tag.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/user-token.repository.ts b/server/src/repositories/user-token.repository.ts index 31d6d2190..074aec332 100644 --- a/server/src/repositories/user-token.repository.ts +++ b/server/src/repositories/user-token.repository.ts @@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { UserTokenEntity } from 'src/entities/user-token.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IUserTokenRepository } from 'src/interfaces/user-token.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index bd94e71a5..aaadc637f 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -3,13 +3,13 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetEntity } from 'src/entities/asset.entity'; import { UserEntity } from 'src/entities/user.entity'; -import { Instrumentation } from 'src/infra/instrumentation'; import { IUserRepository, UserFindOptions, UserListFilter, UserStatsQueryResponse, } from 'src/interfaces/user.repository'; +import { Instrumentation } from 'src/utils/instrumentation'; import { IsNull, Not, Repository } from 'typeorm'; @Instrumentation() diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index ea7178bed..3ae5ebd74 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -21,7 +21,7 @@ import { IAccessRepository } from 'src/interfaces/access.repository'; import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; -import { setUnion } from 'src/utils'; +import { setUnion } from 'src/utils/set'; @Injectable() export class AlbumService { diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 3a6385c03..c2b6a328e 100644 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -1,13 +1,12 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; import { when } from 'jest-when'; -import { JobName } from 'src/domain/job/job.constants'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/asset.dto'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository'; import { AssetStats, IAssetRepository, TimeBucketSize } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository, JobItem } from 'src/interfaces/job.repository'; +import { IJobRepository, JobItem, JobName } from 'src/interfaces/job.repository'; import { IPartnerRepository } from 'src/interfaces/partner.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 51a587a67..b537eda1c 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -6,9 +6,6 @@ import sanitize from 'sanitize-filename'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { mimeTypes } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; -import { IAssetDeletionJob, ISidecarWriteJob } from 'src/domain/job/job.interface'; import { AssetResponseDto, MemoryLaneResponseDto, @@ -31,17 +28,26 @@ import { UpdateStackParentDto } from 'src/dtos/stack.dto'; import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto'; import { AssetEntity } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository'; import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository, JobItem, JobStatus } from 'src/interfaces/job.repository'; +import { + IAssetDeletionJob, + IJobRepository, + ISidecarWriteJob, + JOBS_ASSET_PAGINATION_SIZE, + JobItem, + JobName, + JobStatus, +} from 'src/interfaces/job.repository'; import { IPartnerRepository } from 'src/interfaces/partner.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; -import { usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; +import { usePagination } from 'src/utils/pagination'; export interface UploadRequest { auth: AuthDto | null; diff --git a/server/src/services/audit.service.ts b/server/src/services/audit.service.ts index b84f3824d..c9e4d1663 100644 --- a/server/src/services/audit.service.ts +++ b/server/src/services/audit.service.ts @@ -1,10 +1,9 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { DateTime } from 'luxon'; import { resolve } from 'node:path'; +import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; -import { AUDIT_LOG_MAX_DURATION } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants'; import { AuditDeletesDto, AuditDeletesResponseDto, @@ -16,16 +15,16 @@ import { import { AuthDto } from 'src/dtos/auth.dto'; import { DatabaseAction } from 'src/entities/audit.entity'; import { AssetPathType, PersonPathType, UserPathType } from 'src/entities/move.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IAuditRepository } from 'src/interfaces/audit.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { JobStatus } from 'src/interfaces/job.repository'; +import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; -import { usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { usePagination } from 'src/utils/pagination'; @Injectable() export class AuditService { diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index dc7565e4d..a7d212bac 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -2,7 +2,7 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; import { IncomingHttpHeaders } from 'node:http'; import { Issuer, generators } from 'openid-client'; import { Socket } from 'socket.io'; -import { AuthType } from 'src/domain/auth/auth.constant'; +import { AuthType } from 'src/constants'; import { AuthDto, SignUpDto } from 'src/dtos/auth.dto'; import { UserEntity } from 'src/entities/user.entity'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 9ae41a4fc..0d3d5b118 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -10,9 +10,6 @@ import cookieParser from 'cookie'; import { DateTime } from 'luxon'; import { IncomingHttpHeaders } from 'node:http'; import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client'; -import { AccessCore, Permission } from 'src/cores/access.core'; -import { SystemConfigCore } from 'src/cores/system-config.core'; -import { UserCore } from 'src/cores/user.core'; import { AuthType, IMMICH_ACCESS_COOKIE, @@ -21,7 +18,10 @@ import { IMMICH_IS_AUTHENTICATED, LOGIN_URL, MOBILE_REDIRECT, -} from 'src/domain/auth/auth.constant'; +} from 'src/constants'; +import { AccessCore, Permission } from 'src/cores/access.core'; +import { SystemConfigCore } from 'src/cores/system-config.core'; +import { UserCore } from 'src/cores/user.core'; import { AuthDeviceResponseDto, AuthDto, @@ -39,7 +39,6 @@ import { import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { SystemConfig } from 'src/entities/system-config.entity'; import { UserEntity } from 'src/entities/user.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IKeyRepository } from 'src/interfaces/api-key.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; @@ -48,7 +47,8 @@ import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserTokenRepository } from 'src/interfaces/user-token.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; -import { HumanReadableSize } from 'src/utils'; +import { HumanReadableSize } from 'src/utils/bytes'; +import { ImmichLogger } from 'src/utils/logger'; export interface LoginDetails { isSecure: boolean; diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index dac857a07..57c1c0821 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -1,7 +1,7 @@ -import { Version, VersionType } from 'src/domain/domain.constant'; -import { ImmichLogger } from 'src/infra/logger'; import { DatabaseExtension, IDatabaseRepository, VectorIndex } from 'src/interfaces/database.repository'; import { DatabaseService } from 'src/services/database.service'; +import { ImmichLogger } from 'src/utils/logger'; +import { Version, VersionType } from 'src/utils/version'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; describe(DatabaseService.name, () => { diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index 7bb16a4e1..9f87228b9 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -1,6 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Version, VersionType } from 'src/domain/domain.constant'; -import { ImmichLogger } from 'src/infra/logger'; import { DatabaseExtension, DatabaseLock, @@ -9,6 +7,8 @@ import { VectorIndex, extName, } from 'src/interfaces/database.repository'; +import { ImmichLogger } from 'src/utils/logger'; +import { Version, VersionType } from 'src/utils/version'; @Injectable() export class DatabaseService { diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index ab8389955..3c4991cd5 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -4,7 +4,7 @@ import { DownloadResponseDto } from 'src/dtos/download.dto'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { DownloadService } from 'src/services/download.service'; -import { CacheControl, ImmichFileResponse } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; diff --git a/server/src/services/download.service.ts b/server/src/services/download.service.ts index b03951764..3ef7dbc4b 100644 --- a/server/src/services/download.service.ts +++ b/server/src/services/download.service.ts @@ -1,7 +1,6 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { parse } from 'node:path'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { mimeTypes } from 'src/domain/domain.constant'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; @@ -9,7 +8,10 @@ import { AssetEntity } from 'src/entities/asset.entity'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { IStorageRepository, ImmichReadStream } from 'src/interfaces/storage.repository'; -import { CacheControl, HumanReadableSize, ImmichFileResponse, usePagination } from 'src/utils'; +import { HumanReadableSize } from 'src/utils/bytes'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; +import { mimeTypes } from 'src/utils/mime-types'; +import { usePagination } from 'src/utils/pagination'; @Injectable() export class DownloadService { diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 4be9786a2..13523a142 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -1,10 +1,17 @@ import { BadRequestException } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { JobCommand, JobName, QueueName } from 'src/domain/job/job.constants'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository, JobHandler, JobItem, JobStatus } from 'src/interfaces/job.repository'; +import { + IJobRepository, + JobCommand, + JobHandler, + JobItem, + JobName, + JobStatus, + QueueName, +} from 'src/interfaces/job.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { JobService } from 'src/services/job.service'; diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 8650b92d4..e6acd54a3 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -1,15 +1,24 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { ConcurrentQueueName, JobCommand, JobName, QueueName } from 'src/domain/job/job.constants'; 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 { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository, JobHandler, JobItem, JobStatus, QueueCleanType } from 'src/interfaces/job.repository'; +import { + ConcurrentQueueName, + IJobRepository, + JobCommand, + JobHandler, + JobItem, + JobName, + JobStatus, + QueueCleanType, + QueueName, +} from 'src/interfaces/job.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class JobService { diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 2382d83c2..46cbef3af 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -3,8 +3,6 @@ import { when } from 'jest-when'; import { R_OK } from 'node:constants'; import { Stats } from 'node:fs'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { JobName } from 'src/domain/job/job.constants'; -import { ILibraryFileJob, ILibraryRefreshJob } from 'src/domain/job/job.interface'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryType } from 'src/entities/library.entity'; @@ -13,7 +11,7 @@ import { UserEntity } from 'src/entities/user.entity'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { IJobRepository, ILibraryFileJob, ILibraryRefreshJob, JobName, JobStatus } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index f7b298797..63788d9a7 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -8,9 +8,6 @@ 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 { mimeTypes } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; -import { IBaseJob, IEntityJob, ILibraryFileJob, ILibraryRefreshJob } from 'src/domain/job/job.interface'; import { CreateLibraryDto, LibraryResponseDto, @@ -25,16 +22,27 @@ import { } from 'src/dtos/library.dto'; import { AssetType } from 'src/entities/asset.entity'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository, WithProperty } from 'src/interfaces/asset.repository'; import { InternalEvent, InternalEventMap } from 'src/interfaces/communication.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { + IBaseJob, + IEntityJob, + IJobRepository, + ILibraryFileJob, + ILibraryRefreshJob, + JOBS_ASSET_PAGINATION_SIZE, + JobName, + JobStatus, +} from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; -import { handlePromiseError, usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; +import { handlePromiseError } from 'src/utils/misc'; +import { usePagination } from 'src/utils/pagination'; import { validateCronExpression } from 'src/validation'; const LIBRARY_SCAN_BATCH_SIZE = 5000; diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index eb9407e12..722d2a31c 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,5 +1,4 @@ import { Stats } from 'node:fs'; -import { JobName } from 'src/domain/job/job.constants'; import { AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { @@ -13,7 +12,7 @@ import { } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 5c217f2a8..107ac7c73 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -1,18 +1,6 @@ import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants'; -import { IBaseJob, IEntityJob } from 'src/domain/job/job.interface'; -import { - H264Config, - HEVCConfig, - NVENCConfig, - QSVConfig, - RKMPPConfig, - ThumbnailConfig, - VAAPIConfig, - VP9Config, -} from 'src/domain/media/media.util'; import { SystemConfigFFmpegDto } from 'src/dtos/system-config-ffmpeg.dto'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; @@ -24,10 +12,18 @@ import { TranscodeTarget, VideoCodec, } from 'src/entities/system-config.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository, JobItem, JobStatus } from 'src/interfaces/job.repository'; +import { + IBaseJob, + IEntityJob, + IJobRepository, + JOBS_ASSET_PAGINATION_SIZE, + JobItem, + JobName, + JobStatus, + QueueName, +} from 'src/interfaces/job.repository'; import { AudioStreamInfo, IMediaRepository, @@ -38,7 +34,18 @@ import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; -import { usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { + H264Config, + HEVCConfig, + NVENCConfig, + QSVConfig, + RKMPPConfig, + ThumbnailConfig, + VAAPIConfig, + VP9Config, +} from 'src/utils/media'; +import { usePagination } from 'src/utils/pagination'; @Injectable() export class MediaService { diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index a235aca04..29347f70c 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -3,7 +3,6 @@ import { when } from 'jest-when'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; -import { JobName } from 'src/domain/job/job.constants'; import { AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; import { SystemConfigKey } from 'src/entities/system-config.entity'; @@ -12,7 +11,7 @@ import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.reposito import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 0e949626c..7f9e389bb 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -8,24 +8,32 @@ import path from 'node:path'; import { Subscription } from 'rxjs'; import { StorageCore } from 'src/cores/storage.core'; import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants'; -import { IBaseJob, IEntityJob, ISidecarWriteJob } from 'src/domain/job/job.interface'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { + IBaseJob, + IEntityJob, + IJobRepository, + ISidecarWriteJob, + JOBS_ASSET_PAGINATION_SIZE, + JobName, + JobStatus, + QueueName, +} from 'src/interfaces/job.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; -import { handlePromiseError, usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { handlePromiseError } from 'src/utils/misc'; +import { usePagination } from 'src/utils/pagination'; /** look for a date from these tags (in order) */ const EXIF_DATE_TAGS: Array = [ diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 7abc9f3d9..ae391fa2b 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -1,12 +1,11 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; -import { JobName } from 'src/domain/job/job.constants'; import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { PersonResponseDto, mapFaces, mapPerson } from 'src/dtos/person.dto'; import { AssetFaceEntity } from 'src/entities/asset-face.entity'; import { Colorspace, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; @@ -15,7 +14,7 @@ import { FaceSearchResult, ISearchRepository } from 'src/interfaces/search.repos import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { PersonService } from 'src/services/person.service'; -import { CacheControl, ImmichFileResponse } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { faceStub } from 'test/fixtures/face.stub'; diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index 7da482966..99eae8c06 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -1,11 +1,8 @@ import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { FACE_THUMBNAIL_SIZE } from 'src/constants'; import { AccessCore, Permission } from 'src/cores/access.core'; import { StorageCore } from 'src/cores/storage.core'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { mimeTypes } from 'src/domain/domain.constant'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants'; -import { IBaseJob, IDeferrableJob, IEntityJob } from 'src/domain/job/job.interface'; -import { FACE_THUMBNAIL_SIZE } from 'src/domain/media/media.constant'; import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -26,11 +23,20 @@ import { } from 'src/dtos/person.dto'; import { PersonPathType } from 'src/entities/move.entity'; import { PersonEntity } from 'src/entities/person.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository, JobItem, JobStatus } from 'src/interfaces/job.repository'; +import { + IBaseJob, + IDeferrableJob, + IEntityJob, + IJobRepository, + JOBS_ASSET_PAGINATION_SIZE, + JobItem, + JobName, + JobStatus, + QueueName, +} from 'src/interfaces/job.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; import { CropOptions, IMediaRepository } from 'src/interfaces/media.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; @@ -38,7 +44,10 @@ import { IPersonRepository, UpdateFacesData } from 'src/interfaces/person.reposi import { ISearchRepository } from 'src/interfaces/search.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; -import { CacheControl, ImmichFileResponse, usePagination } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; +import { usePagination } from 'src/utils/pagination'; import { IsNull } from 'typeorm'; @Injectable() diff --git a/server/src/services/server-info.service.spec.ts b/server/src/services/server-info.service.spec.ts index 0db7597b8..fe678dbef 100644 --- a/server/src/services/server-info.service.spec.ts +++ b/server/src/services/server-info.service.spec.ts @@ -1,4 +1,4 @@ -import { serverVersion } from 'src/domain/domain.constant'; +import { serverVersion } from 'src/constants'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { ICommunicationRepository } from 'src/interfaces/communication.repository'; import { IServerInfoRepository } from 'src/interfaces/server-info.repository'; diff --git a/server/src/services/server-info.service.ts b/server/src/services/server-info.service.ts index a76ae4022..e0a62bdc7 100644 --- a/server/src/services/server-info.service.ts +++ b/server/src/services/server-info.service.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; 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 { Version, isDev, mimeTypes, serverVersion } from 'src/domain/domain.constant'; import { ServerConfigDto, ServerFeaturesDto, @@ -13,14 +13,16 @@ import { UsageByUserDto, } from 'src/dtos/server-info.dto'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; import { IServerInfoRepository } from 'src/interfaces/server-info.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository'; import { IUserRepository, UserStatsQueryResponse } from 'src/interfaces/user.repository'; -import { asHumanReadable } from 'src/utils'; +import { asHumanReadable } from 'src/utils/bytes'; +import { ImmichLogger } from 'src/utils/logger'; +import { mimeTypes } from 'src/utils/mime-types'; +import { Version } from 'src/utils/version'; @Injectable() export class ServerInfoService { diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index a84a1f120..44b1f91e5 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -16,7 +16,7 @@ import { SharedLinkEntity, SharedLinkType } from 'src/entities/shared-link.entit import { IAccessRepository } from 'src/interfaces/access.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository'; -import { OpenGraphTags } from 'src/utils'; +import { OpenGraphTags } from 'src/utils/misc'; @Injectable() export class SharedLinkService { diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index a1b8943f2..b74211da3 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,14 +1,13 @@ -import { JobName } from 'src/domain/job/job.constants'; -import { cleanModelName, getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant'; import { AssetEntity } from 'src/entities/asset.entity'; import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName } from 'src/interfaces/job.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { SmartInfoService } from 'src/services/smart-info.service'; +import { getCLIPModelInfo } from 'src/utils/misc'; import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; @@ -124,16 +123,14 @@ describe(SmartInfoService.name, () => { }); }); - describe('cleanModelName', () => { - it('should clean name', () => { - expect(cleanModelName('ViT-B-32::openai')).toEqual('ViT-B-32__openai'); - expect(cleanModelName('M-CLIP/XLM-Roberta-Large-Vit-L-14')).toEqual('XLM-Roberta-Large-Vit-L-14'); - }); - }); - describe('getCLIPModelInfo', () => { it('should return the model info', () => { expect(getCLIPModelInfo('ViT-B-32__openai')).toEqual({ dimSize: 512 }); + expect(getCLIPModelInfo('M-CLIP/XLM-Roberta-Large-Vit-L-14')).toEqual({ dimSize: 768 }); + }); + + it('should clean the model name', () => { + expect(getCLIPModelInfo('ViT-B-32::openai')).toEqual({ dimSize: 512 }); }); it('should throw an error if the model is not present', () => { diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index a7d470008..d686cd56d 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -1,15 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { SystemConfigCore } from 'src/cores/system-config.core'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants'; -import { IBaseJob, IEntityJob } from 'src/domain/job/job.interface'; -import { ImmichLogger } from 'src/infra/logger'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.repository'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { + IBaseJob, + IEntityJob, + IJobRepository, + JOBS_ASSET_PAGINATION_SIZE, + JobName, + JobStatus, + QueueName, +} from 'src/interfaces/job.repository'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; -import { usePagination } from 'src/utils'; +import { ImmichLogger } from 'src/utils/logger'; +import { usePagination } from 'src/utils/pagination'; @Injectable() export class SmartInfoService { diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index b4af03a9f..43c6eb09e 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -4,10 +4,6 @@ import handlebar from 'handlebars'; import { DateTime } from 'luxon'; import path from 'node:path'; import sanitize from 'sanitize-filename'; -import { StorageCore, StorageFolder } from 'src/cores/storage.core'; -import { SystemConfigCore } from 'src/cores/system-config.core'; -import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants'; -import { IEntityJob } from 'src/domain/job/job.interface'; import { supportedDayTokens, supportedHourTokens, @@ -16,23 +12,26 @@ import { supportedSecondTokens, supportedWeekTokens, supportedYearTokens, -} from 'src/domain/system-config/system-config.constants'; +} from 'src/constants'; +import { StorageCore, StorageFolder } from 'src/cores/storage.core'; +import { SystemConfigCore } from 'src/cores/system-config.core'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; import { SystemConfig } from 'src/entities/system-config.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { InternalEvent, InternalEventMap } from 'src/interfaces/communication.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.repository'; -import { JobStatus } from 'src/interfaces/job.repository'; +import { IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.repository'; import { IMoveRepository } from 'src/interfaces/move.repository'; import { IPersonRepository } from 'src/interfaces/person.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; -import { getLivePhotoMotionFilename, usePagination } from 'src/utils'; +import { getLivePhotoMotionFilename } from 'src/utils/file'; +import { ImmichLogger } from 'src/utils/logger'; +import { usePagination } from 'src/utils/pagination'; export interface MoveAssetMetadata { storageLabel: string | null; diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 9df3dd778..22cac0040 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; -import { IDeleteFilesJob } from 'src/domain/job/job.interface'; -import { ImmichLogger } from 'src/infra/logger'; -import { JobStatus } from 'src/interfaces/job.repository'; +import { IDeleteFilesJob, JobStatus } from 'src/interfaces/job.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; +import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class StorageService { diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index b5b5875d7..d7896e87e 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -1,6 +1,5 @@ import { BadRequestException } from '@nestjs/common'; import { defaults } from 'src/cores/system-config.core'; -import { QueueName } from 'src/domain/job/job.constants'; import { AudioCodec, CQMode, @@ -14,11 +13,12 @@ import { TranscodePolicy, VideoCodec, } from 'src/entities/system-config.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { ICommunicationRepository, ServerEvent } from 'src/interfaces/communication.repository'; +import { QueueName } from 'src/interfaces/job.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { SystemConfigService } from 'src/services/system-config.service'; +import { ImmichLogger } from 'src/utils/logger'; import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index c5837dca2..acb8b13fb 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -2,7 +2,6 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { instanceToPlain } from 'class-transformer'; import _ from 'lodash'; -import { SystemConfigCore } from 'src/cores/system-config.core'; import { supportedDayTokens, supportedHourTokens, @@ -12,11 +11,11 @@ import { supportedSecondTokens, supportedWeekTokens, supportedYearTokens, -} from 'src/domain/system-config/system-config.constants'; +} from 'src/constants'; +import { SystemConfigCore } from 'src/cores/system-config.core'; import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config-storage-template.dto'; import { SystemConfigDto, mapConfig } from 'src/dtos/system-config.dto'; import { LogLevel, SystemConfig } from 'src/entities/system-config.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { ClientEvent, ICommunicationRepository, @@ -26,6 +25,7 @@ import { } from 'src/interfaces/communication.repository'; import { ISearchRepository } from 'src/interfaces/search.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; +import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class SystemConfigService { diff --git a/server/src/services/trash.service.spec.ts b/server/src/services/trash.service.spec.ts index 09f668e6f..03e87a8d0 100644 --- a/server/src/services/trash.service.spec.ts +++ b/server/src/services/trash.service.spec.ts @@ -1,8 +1,7 @@ import { BadRequestException } from '@nestjs/common'; -import { JobName } from 'src/domain/job/job.constants'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName } from 'src/interfaces/job.repository'; import { TrashService } from 'src/services/trash.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; diff --git a/server/src/services/trash.service.ts b/server/src/services/trash.service.ts index 9bece663b..61de73097 100644 --- a/server/src/services/trash.service.ts +++ b/server/src/services/trash.service.ts @@ -1,14 +1,13 @@ import { Inject } from '@nestjs/common'; import { DateTime } from 'luxon'; import { AccessCore, Permission } from 'src/cores/access.core'; -import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { IAccessRepository } from 'src/interfaces/access.repository'; import { IAssetRepository } from 'src/interfaces/asset.repository'; import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; -import { usePagination } from 'src/utils'; +import { IJobRepository, JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/interfaces/job.repository'; +import { usePagination } from 'src/utils/pagination'; export class TrashService { private access: AccessCore; diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index 51457b201..6a639003d 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -5,18 +5,17 @@ import { NotFoundException, } from '@nestjs/common'; import { when } from 'jest-when'; -import { JobName } from 'src/domain/job/job.constants'; import { UpdateUserDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository } from 'src/interfaces/job.repository'; +import { IJobRepository, JobName } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository } from 'src/interfaces/user.repository'; import { UserService } from 'src/services/user.service'; -import { CacheControl, ImmichFileResponse } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; import { authStub } from 'test/fixtures/auth.stub'; import { systemConfigStub } from 'test/fixtures/system-config.stub'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index ef742123f..e4d8f11c4 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -4,21 +4,19 @@ 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'; -import { JobName } from 'src/domain/job/job.constants'; -import { IEntityJob } from 'src/domain/job/job.interface'; import { AuthDto } from 'src/dtos/auth.dto'; import { CreateProfileImageResponseDto, mapCreateProfileImageResponse } from 'src/dtos/user-profile.dto'; import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; -import { ImmichLogger } from 'src/infra/logger'; import { IAlbumRepository } from 'src/interfaces/album.repository'; import { ICryptoRepository } from 'src/interfaces/crypto.repository'; -import { IJobRepository, JobStatus } from 'src/interfaces/job.repository'; +import { IEntityJob, IJobRepository, JobName, JobStatus } from 'src/interfaces/job.repository'; import { ILibraryRepository } from 'src/interfaces/library.repository'; import { IStorageRepository } from 'src/interfaces/storage.repository'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { IUserRepository, UserFindOptions } from 'src/interfaces/user.repository'; -import { CacheControl, ImmichFileResponse } from 'src/utils'; +import { CacheControl, ImmichFileResponse } from 'src/utils/file'; +import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class UserService { diff --git a/server/src/utils.spec.ts b/server/src/utils.spec.ts deleted file mode 100644 index c5ae5f6e5..000000000 --- a/server/src/utils.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { isDecimalNumber, isNumberInRange, parseLatitude, parseLongitude, toNumberOrNull } from 'src/utils'; - -describe('checks if a number is a decimal number', () => { - it('returns false for non-decimal numbers', () => { - expect(isDecimalNumber(Number.NaN)).toBe(false); - expect(isDecimalNumber(Number.POSITIVE_INFINITY)).toBe(false); - expect(isDecimalNumber(Number.NEGATIVE_INFINITY)).toBe(false); - }); - - it('returns true for decimal numbers', () => { - expect(isDecimalNumber(0)).toBe(true); - expect(isDecimalNumber(-0)).toBe(true); - expect(isDecimalNumber(10.123_45)).toBe(true); - expect(isDecimalNumber(Number.MAX_VALUE)).toBe(true); - expect(isDecimalNumber(Number.MIN_VALUE)).toBe(true); - }); -}); - -describe('checks if a number is within a range', () => { - it('returns false for numbers outside the range', () => { - expect(isNumberInRange(0, 10, 10)).toBe(false); - expect(isNumberInRange(0.01, 10, 10)).toBe(false); - expect(isNumberInRange(50.1, 0, 50)).toBe(false); - }); - - it('returns true for numbers inside the range', () => { - expect(isNumberInRange(0, 0, 50)).toBe(true); - expect(isNumberInRange(50, 0, 50)).toBe(true); - expect(isNumberInRange(-50.123_45, -50.123_45, 0)).toBe(true); - }); -}); - -describe('converts input to a number or null', () => { - it('returns null for invalid inputs', () => { - expect(toNumberOrNull(null)).toBeNull(); - // eslint-disable-next-line unicorn/no-useless-undefined - expect(toNumberOrNull(undefined)).toBeNull(); - expect(toNumberOrNull('')).toBeNull(); - expect(toNumberOrNull(Number.NaN)).toBeNull(); - }); - - it('returns a number for valid inputs', () => { - expect(toNumberOrNull(0)).toBeCloseTo(0); - expect(toNumberOrNull('0')).toBeCloseTo(0); - expect(toNumberOrNull('-123.45')).toBeCloseTo(-123.45); - }); -}); - -describe('parsing latitude from string input', () => { - it('returns null for invalid inputs', () => { - expect(parseLatitude('')).toBeNull(); - expect(parseLatitude('NaN')).toBeNull(); - expect(parseLatitude('Infinity')).toBeNull(); - expect(parseLatitude('-Infinity')).toBeNull(); - expect(parseLatitude('90.001')).toBeNull(); - expect(parseLatitude(-90.000_001)).toBeNull(); - expect(parseLatitude('1000')).toBeNull(); - expect(parseLatitude(-1000)).toBeNull(); - }); - - it('returns the numeric coordinate for valid inputs', () => { - expect(parseLatitude('90')).toBeCloseTo(90); - expect(parseLatitude('-90')).toBeCloseTo(-90); - expect(parseLatitude(89.999_999)).toBeCloseTo(89.999_999); - expect(parseLatitude('-89.9')).toBeCloseTo(-89.9); - expect(parseLatitude(0)).toBeCloseTo(0); - expect(parseLatitude('-0.0')).toBeCloseTo(-0); - }); -}); - -describe('parsing latitude from null input', () => { - it('returns null for null input', () => { - expect(parseLatitude(null)).toBeNull(); - }); -}); - -describe('parsing longitude from string input', () => { - it('returns null for invalid inputs', () => { - expect(parseLongitude('')).toBeNull(); - expect(parseLongitude('NaN')).toBeNull(); - expect(parseLongitude(Number.POSITIVE_INFINITY)).toBeNull(); - expect(parseLongitude('-Infinity')).toBeNull(); - expect(parseLongitude('180.001')).toBeNull(); - expect(parseLongitude('-180.000001')).toBeNull(); - expect(parseLongitude(1000)).toBeNull(); - expect(parseLongitude('-1000')).toBeNull(); - }); - - it('returns the numeric coordinate for valid inputs', () => { - expect(parseLongitude(180)).toBeCloseTo(180); - expect(parseLongitude('-180')).toBeCloseTo(-180); - expect(parseLongitude('179.999999')).toBeCloseTo(179.999_999); - expect(parseLongitude(-179.9)).toBeCloseTo(-179.9); - expect(parseLongitude('0')).toBeCloseTo(0); - expect(parseLongitude('-0.0')).toBeCloseTo(-0); - }); -}); - -describe('parsing longitude from null input', () => { - it('returns null for null input', () => { - expect(parseLongitude(null)).toBeNull(); - }); -}); diff --git a/server/src/utils.ts b/server/src/utils.ts deleted file mode 100644 index 863dde7ca..000000000 --- a/server/src/utils.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { basename, extname } from 'node:path'; -import { ImmichLogger } from 'src/infra/logger'; - -const KiB = Math.pow(1024, 1); -const MiB = Math.pow(1024, 2); -const GiB = Math.pow(1024, 3); -const TiB = Math.pow(1024, 4); -const PiB = Math.pow(1024, 5); - -export const HumanReadableSize = { KiB, MiB, GiB, TiB, PiB }; - -export function asHumanReadable(bytes: number, precision = 1): string { - const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']; - - let magnitude = 0; - let remainder = bytes; - while (remainder >= 1024) { - if (magnitude + 1 < units.length) { - magnitude++; - remainder /= 1024; - } else { - break; - } - } - - return `${remainder.toFixed(magnitude == 0 ? 0 : precision)} ${units[magnitude]}`; -} - -export const isConnectionAborted = (error: Error | any) => error.code === 'ECONNABORTED'; - -export function isDecimalNumber(number_: number): boolean { - return !Number.isNaN(number_) && Number.isFinite(number_); -} - -/** - * Check if `num` is a valid number and is between `start` and `end` (inclusive) - */ -export function isNumberInRange(number_: number, start: number, end: number): boolean { - return isDecimalNumber(number_) && number_ >= start && number_ <= end; -} - -export function toNumberOrNull(input: number | string | null | undefined): number | null { - if (input === null || input === undefined) { - return null; - } - - const number_ = typeof input === 'string' ? Number.parseFloat(input) : input; - return isDecimalNumber(number_) ? number_ : null; -} - -export function parseLatitude(input: string | number | null): number | null { - if (input === null) { - return null; - } - const latitude = typeof input === 'string' ? Number.parseFloat(input) : input; - - if (isNumberInRange(latitude, -90, 90)) { - return latitude; - } - return null; -} - -export function parseLongitude(input: string | number | null): number | null { - if (input === null) { - return null; - } - - const longitude = typeof input === 'string' ? Number.parseFloat(input) : input; - - if (isNumberInRange(longitude, -180, 180)) { - return longitude; - } - return null; -} - -// NOTE: The following Set utils have been added here, to easily determine where they are used. -// They should be replaced with native Set operations, when they are added to the language. -// Proposal reference: https://github.com/tc39/proposal-set-methods - -export const setUnion = (...sets: Set[]): Set => { - const union = new Set(sets[0]); - for (const set of sets.slice(1)) { - for (const element of set) { - union.add(element); - } - } - return union; -}; - -export const setDifference = (setA: Set, ...sets: Set[]): Set => { - const difference = new Set(setA); - for (const set of sets) { - for (const element of set) { - difference.delete(element); - } - } - return difference; -}; - -export const setIsSuperset = (set: Set, subset: Set): boolean => { - for (const element of subset) { - if (!set.has(element)) { - return false; - } - } - return true; -}; - -export const setIsEqual = (setA: Set, setB: Set): boolean => { - return setA.size === setB.size && setIsSuperset(setA, setB); -}; - -export const handlePromiseError = (promise: Promise, logger: ImmichLogger): void => { - promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack)); -}; - -export enum CacheControl { - PRIVATE_WITH_CACHE = 'private_with_cache', - PRIVATE_WITHOUT_CACHE = 'private_without_cache', - NONE = 'none', -} - -export class ImmichFileResponse { - public readonly path!: string; - public readonly contentType!: string; - public readonly cacheControl!: CacheControl; - - constructor(response: ImmichFileResponse) { - Object.assign(this, response); - } -} - -export interface OpenGraphTags { - title: string; - description: string; - imageUrl?: string; -} - -export function getFileNameWithoutExtension(path: string): string { - return basename(path, extname(path)); -} - -export function getLivePhotoMotionFilename(stillName: string, motionName: string) { - return getFileNameWithoutExtension(stillName) + extname(motionName); -} - -export interface PaginationOptions { - take: number; - skip?: number; -} - -export enum PaginationMode { - LIMIT_OFFSET = 'limit-offset', - SKIP_TAKE = 'skip-take', -} - -export interface PaginatedBuilderOptions { - take: number; - skip?: number; - mode?: PaginationMode; -} - -export interface PaginationResult { - items: T[]; - hasNextPage: boolean; -} - -export type Paginated = Promise>; - -export async function* usePagination( - pageSize: number, - getNextPage: (pagination: PaginationOptions) => PaginationResult | Paginated, -) { - let hasNextPage = true; - - for (let skip = 0; hasNextPage; skip += pageSize) { - const result = await getNextPage({ take: pageSize, skip }); - hasNextPage = result.hasNextPage; - yield result.items; - } -} diff --git a/server/src/utils/bytes.ts b/server/src/utils/bytes.ts new file mode 100644 index 000000000..e837c81b9 --- /dev/null +++ b/server/src/utils/bytes.ts @@ -0,0 +1,24 @@ +const KiB = Math.pow(1024, 1); +const MiB = Math.pow(1024, 2); +const GiB = Math.pow(1024, 3); +const TiB = Math.pow(1024, 4); +const PiB = Math.pow(1024, 5); + +export const HumanReadableSize = { KiB, MiB, GiB, TiB, PiB }; + +export function asHumanReadable(bytes: number, precision = 1): string { + const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']; + + let magnitude = 0; + let remainder = bytes; + while (remainder >= 1024) { + if (magnitude + 1 < units.length) { + magnitude++; + remainder /= 1024; + } else { + break; + } + } + + return `${remainder.toFixed(magnitude == 0 ? 0 : precision)} ${units[magnitude]}`; +} diff --git a/server/src/infra/infra.utils.ts b/server/src/utils/database.ts similarity index 75% rename from server/src/infra/infra.utils.ts rename to server/src/utils/database.ts index 2b75af379..3a3e4603b 100644 --- a/server/src/infra/infra.utils.ts +++ b/server/src/utils/database.ts @@ -1,18 +1,7 @@ import _ from 'lodash'; import { AssetEntity } from 'src/entities/asset.entity'; import { AssetSearchBuilderOptions } from 'src/interfaces/search.repository'; -import { Paginated, PaginatedBuilderOptions, PaginationMode, PaginationOptions, PaginationResult } from 'src/utils'; -import { - Between, - FindManyOptions, - IsNull, - LessThanOrEqual, - MoreThanOrEqual, - Not, - ObjectLiteral, - Repository, - SelectQueryBuilder, -} from 'typeorm'; +import { Between, IsNull, LessThanOrEqual, MoreThanOrEqual, Not, SelectQueryBuilder } from 'typeorm'; /** * Allows optional values unlike the regular Between and uses MoreThanOrEqual @@ -28,47 +17,6 @@ export function OptionalBetween(from?: T, to?: T) { } } -function paginationHelper(items: Entity[], take: number): PaginationResult { - const hasNextPage = items.length > take; - items.splice(take); - - return { items, hasNextPage }; -} - -export async function paginate( - repository: Repository, - { take, skip }: PaginationOptions, - searchOptions?: FindManyOptions, -): Paginated { - const items = await repository.find( - _.omitBy( - { - ...searchOptions, - // Take one more item to check if there's a next page - take: take + 1, - skip, - }, - _.isUndefined, - ), - ); - - return paginationHelper(items, take); -} - -export async function paginatedBuilder( - qb: SelectQueryBuilder, - { take, skip, mode }: PaginatedBuilderOptions, -): Paginated { - if (mode === PaginationMode.LIMIT_OFFSET) { - qb.limit(take + 1).offset(skip); - } else { - qb.take(take + 1).skip(skip); - } - - const items = await qb.getMany(); - return paginationHelper(items, take); -} - export const asVector = (embedding: number[], quote = false) => quote ? `'[${embedding.join(',')}]'` : `[${embedding.join(',')}]`; diff --git a/server/src/utils/file.ts b/server/src/utils/file.ts new file mode 100644 index 000000000..0a39bdc67 --- /dev/null +++ b/server/src/utils/file.ts @@ -0,0 +1,25 @@ +import { basename, extname } from 'node:path'; + +export function getFileNameWithoutExtension(path: string): string { + return basename(path, extname(path)); +} + +export function getLivePhotoMotionFilename(stillName: string, motionName: string) { + return getFileNameWithoutExtension(stillName) + extname(motionName); +} + +export enum CacheControl { + PRIVATE_WITH_CACHE = 'private_with_cache', + PRIVATE_WITHOUT_CACHE = 'private_without_cache', + NONE = 'none', +} + +export class ImmichFileResponse { + public readonly path!: string; + public readonly contentType!: string; + public readonly cacheControl!: CacheControl; + + constructor(response: ImmichFileResponse) { + Object.assign(this, response); + } +} diff --git a/server/src/infra/instrumentation.ts b/server/src/utils/instrumentation.ts similarity index 98% rename from server/src/infra/instrumentation.ts rename to server/src/utils/instrumentation.ts index a30f0523a..aa39a7728 100644 --- a/server/src/infra/instrumentation.ts +++ b/server/src/utils/instrumentation.ts @@ -14,8 +14,8 @@ import { OpenTelemetryModuleOptions } from 'nestjs-otel/lib/interfaces'; import { copyMetadataFromFunctionToFunction } from 'nestjs-otel/lib/opentelemetry.utils'; import { performance } from 'node:perf_hooks'; import { excludePaths } from 'src/config'; +import { serverVersion } from 'src/constants'; import { DecorateAll } from 'src/decorators'; -import { serverVersion } from 'src/domain/domain.constant'; let metricsEnabled = process.env.IMMICH_METRICS === 'true'; const hostMetrics = diff --git a/server/src/infra/logger.ts b/server/src/utils/logger.ts similarity index 100% rename from server/src/infra/logger.ts rename to server/src/utils/logger.ts diff --git a/server/src/domain/media/media.util.ts b/server/src/utils/media.ts similarity index 100% rename from server/src/domain/media/media.util.ts rename to server/src/utils/media.ts diff --git a/server/src/domain/domain.constant.spec.ts b/server/src/utils/mime-types.spec.ts similarity index 68% rename from server/src/domain/domain.constant.spec.ts rename to server/src/utils/mime-types.spec.ts index e8a1c7b72..96c1921ba 100644 --- a/server/src/domain/domain.constant.spec.ts +++ b/server/src/utils/mime-types.spec.ts @@ -1,4 +1,4 @@ -import { mimeTypes, Version, VersionType } from 'src/domain/domain.constant'; +import { mimeTypes } from 'src/utils/mime-types'; describe('mimeTypes', () => { for (const { mimetype, extension } of [ @@ -196,74 +196,3 @@ describe('mimeTypes', () => { } }); }); - -describe('Version', () => { - const tests = [ - { this: new Version(0, 0, 1), other: new Version(0, 0, 0), compare: 1, type: VersionType.PATCH }, - { this: new Version(0, 1, 0), other: new Version(0, 0, 0), compare: 1, type: VersionType.MINOR }, - { this: new Version(1, 0, 0), other: new Version(0, 0, 0), compare: 1, type: VersionType.MAJOR }, - { this: new Version(0, 0, 0), other: new Version(0, 0, 1), compare: -1, type: VersionType.PATCH }, - { this: new Version(0, 0, 0), other: new Version(0, 1, 0), compare: -1, type: VersionType.MINOR }, - { this: new Version(0, 0, 0), other: new Version(1, 0, 0), compare: -1, type: VersionType.MAJOR }, - { this: new Version(0, 0, 0), other: new Version(0, 0, 0), compare: 0, type: VersionType.EQUAL }, - { this: new Version(0, 0, 1), other: new Version(0, 0, 1), compare: 0, type: VersionType.EQUAL }, - { this: new Version(0, 1, 0), other: new Version(0, 1, 0), compare: 0, type: VersionType.EQUAL }, - { this: new Version(1, 0, 0), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, - { this: new Version(1, 0), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, - { this: new Version(1, 0), other: new Version(1, 0, 1), compare: -1, type: VersionType.PATCH }, - { this: new Version(1, 1), other: new Version(1, 0, 1), compare: 1, type: VersionType.MINOR }, - { this: new Version(1), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, - { this: new Version(1), other: new Version(1, 0, 1), compare: -1, type: VersionType.PATCH }, - ]; - - describe('isOlderThan', () => { - for (const { this: thisVersion, other: otherVersion, compare, type } of tests) { - const expected = compare < 0 ? type : VersionType.EQUAL; - it(`should return '${expected}' when comparing ${thisVersion} to ${otherVersion}`, () => { - expect(thisVersion.isOlderThan(otherVersion)).toEqual(expected); - }); - } - }); - - describe('isEqual', () => { - for (const { this: thisVersion, other: otherVersion, compare } of tests) { - const bool = compare === 0; - it(`should return ${bool} when comparing ${thisVersion} to ${otherVersion}`, () => { - expect(thisVersion.isEqual(otherVersion)).toEqual(bool); - }); - } - }); - - describe('isNewerThan', () => { - for (const { this: thisVersion, other: otherVersion, compare, type } of tests) { - const expected = compare > 0 ? type : VersionType.EQUAL; - it(`should return ${expected} when comparing ${thisVersion} to ${otherVersion}`, () => { - expect(thisVersion.isNewerThan(otherVersion)).toEqual(expected); - }); - } - }); - - describe('fromString', () => { - const tests = [ - { scenario: 'leading v', value: 'v1.72.2', expected: new Version(1, 72, 2) }, - { scenario: 'uppercase v', value: 'V1.72.2', expected: new Version(1, 72, 2) }, - { scenario: 'missing v', value: '1.72.2', expected: new Version(1, 72, 2) }, - { scenario: 'large patch', value: '1.72.123', expected: new Version(1, 72, 123) }, - { scenario: 'large minor', value: '1.123.0', expected: new Version(1, 123, 0) }, - { scenario: 'large major', value: '123.0.0', expected: new Version(123, 0, 0) }, - { scenario: 'major bump', value: 'v2.0.0', expected: new Version(2, 0, 0) }, - { scenario: 'has dash', value: '14.10-1', expected: new Version(14, 10, 1) }, - { scenario: 'missing patch', value: '14.10', expected: new Version(14, 10, 0) }, - { scenario: 'only major', value: '14', expected: new Version(14, 0, 0) }, - ]; - - for (const { scenario, value, expected } of tests) { - it(`should correctly parse ${scenario}`, () => { - const actual = Version.fromString(value); - expect(actual.major).toEqual(expected.major); - expect(actual.minor).toEqual(expected.minor); - expect(actual.patch).toEqual(expected.patch); - }); - } - }); -}); diff --git a/server/src/domain/domain.constant.ts b/server/src/utils/mime-types.ts similarity index 56% rename from server/src/domain/domain.constant.ts rename to server/src/utils/mime-types.ts index 29c4837b6..789fc6a1c 100644 --- a/server/src/domain/domain.constant.ts +++ b/server/src/utils/mime-types.ts @@ -1,97 +1,6 @@ -import { Duration } from 'luxon'; -import { readFileSync } from 'node:fs'; -import { extname, join } from 'node:path'; +import { extname } from 'node:path'; import { AssetType } from 'src/entities/asset.entity'; -export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); -export const ONE_HOUR = Duration.fromObject({ hours: 1 }); - -export interface IVersion { - major: number; - minor: number; - patch: number; -} - -export enum VersionType { - EQUAL = 0, - PATCH = 1, - MINOR = 2, - MAJOR = 3, -} - -export class Version implements IVersion { - public readonly types = ['major', 'minor', 'patch'] as const; - - constructor( - public major: number, - public minor: number = 0, - public patch: number = 0, - ) {} - - toString() { - return `${this.major}.${this.minor}.${this.patch}`; - } - - toJSON() { - const { major, minor, patch } = this; - return { major, minor, patch }; - } - - static fromString(version: string): Version { - const regex = /v?(?\d+)(?:\.(?\d+))?(?:[.-](?\d+))?/i; - const matchResult = version.match(regex); - if (matchResult) { - const { major, minor = '0', patch = '0' } = matchResult.groups as { [K in keyof IVersion]: string }; - return new Version(Number(major), Number(minor), Number(patch)); - } else { - throw new Error(`Invalid version format: ${version}`); - } - } - - private compare(version: Version): [number, VersionType] { - for (const [i, key] of this.types.entries()) { - const diff = this[key] - version[key]; - if (diff !== 0) { - return [diff > 0 ? 1 : -1, (VersionType.MAJOR - i) as VersionType]; - } - } - - return [0, VersionType.EQUAL]; - } - - isOlderThan(version: Version): VersionType { - const [bool, type] = this.compare(version); - return bool < 0 ? type : VersionType.EQUAL; - } - - isEqual(version: Version): boolean { - const [bool] = this.compare(version); - return bool === 0; - } - - isNewerThan(version: Version): VersionType { - const [bool, type] = this.compare(version); - return bool > 0 ? type : VersionType.EQUAL; - } -} - -export const envName = (process.env.NODE_ENV || 'development').toUpperCase(); -export const isDev = process.env.NODE_ENV === 'development'; - -const { version } = JSON.parse(readFileSync('./package.json', 'utf8')); -export const serverVersion = Version.fromString(version); - -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; -export const WEB_ROOT = process.env.IMMICH_WEB_ROOT || '/usr/src/app/www'; - -const GEODATA_ROOT_PATH = process.env.IMMICH_REVERSE_GEOCODING_ROOT || '/usr/src/resources'; - -export const citiesFile = 'cities500.txt'; -export const geodataDatePath = join(GEODATA_ROOT_PATH, 'geodata-date.txt'); -export const geodataAdmin1Path = join(GEODATA_ROOT_PATH, 'admin1CodesASCII.txt'); -export const geodataAdmin2Path = join(GEODATA_ROOT_PATH, 'admin2Codes.txt'); -export const geodataCities500Path = join(GEODATA_ROOT_PATH, citiesFile); - const image: Record = { '.3fr': ['image/3fr', 'image/x-hasselblad-3fr'], '.ari': ['image/ari', 'image/x-arriflex-ari'], diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts new file mode 100644 index 000000000..d664a904e --- /dev/null +++ b/server/src/utils/misc.ts @@ -0,0 +1,32 @@ +import { CLIP_MODEL_INFO } from 'src/constants'; +import { ImmichLogger } from 'src/utils/logger'; + +export const isConnectionAborted = (error: Error | any) => error.code === 'ECONNABORTED'; + +export const handlePromiseError = (promise: Promise, logger: ImmichLogger): void => { + promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack)); +}; + +export interface OpenGraphTags { + title: string; + description: string; + imageUrl?: string; +} + +function cleanModelName(modelName: string): string { + const token = modelName.split('/').at(-1); + if (!token) { + throw new Error(`Invalid model name: ${modelName}`); + } + + return token.replaceAll(':', '_'); +} + +export function getCLIPModelInfo(modelName: string) { + const modelInfo = CLIP_MODEL_INFO[cleanModelName(modelName)]; + if (!modelInfo) { + throw new Error(`Unknown CLIP model: ${modelName}`); + } + + return modelInfo; +} diff --git a/server/src/utils/pagination.ts b/server/src/utils/pagination.ts new file mode 100644 index 000000000..dec1a9de0 --- /dev/null +++ b/server/src/utils/pagination.ts @@ -0,0 +1,79 @@ +import _ from 'lodash'; +import { FindManyOptions, ObjectLiteral, Repository, SelectQueryBuilder } from 'typeorm'; + +export interface PaginationOptions { + take: number; + skip?: number; +} + +export enum PaginationMode { + LIMIT_OFFSET = 'limit-offset', + SKIP_TAKE = 'skip-take', +} + +export interface PaginatedBuilderOptions { + take: number; + skip?: number; + mode?: PaginationMode; +} + +export interface PaginationResult { + items: T[]; + hasNextPage: boolean; +} + +export type Paginated = Promise>; + +export async function* usePagination( + pageSize: number, + getNextPage: (pagination: PaginationOptions) => PaginationResult | Paginated, +) { + let hasNextPage = true; + + for (let skip = 0; hasNextPage; skip += pageSize) { + const result = await getNextPage({ take: pageSize, skip }); + hasNextPage = result.hasNextPage; + yield result.items; + } +} + +function paginationHelper(items: Entity[], take: number): PaginationResult { + const hasNextPage = items.length > take; + items.splice(take); + + return { items, hasNextPage }; +} + +export async function paginate( + repository: Repository, + { take, skip }: PaginationOptions, + searchOptions?: FindManyOptions, +): Paginated { + const items = await repository.find( + _.omitBy( + { + ...searchOptions, + // Take one more item to check if there's a next page + take: take + 1, + skip, + }, + _.isUndefined, + ), + ); + + return paginationHelper(items, take); +} + +export async function paginatedBuilder( + qb: SelectQueryBuilder, + { take, skip, mode }: PaginatedBuilderOptions, +): Paginated { + if (mode === PaginationMode.LIMIT_OFFSET) { + qb.limit(take + 1).offset(skip); + } else { + qb.take(take + 1).skip(skip); + } + + const items = await qb.getMany(); + return paginationHelper(items, take); +} diff --git a/server/src/utils/set.ts b/server/src/utils/set.ts new file mode 100644 index 000000000..971d3f7e5 --- /dev/null +++ b/server/src/utils/set.ts @@ -0,0 +1,36 @@ +// NOTE: The following Set utils have been added here, to easily determine where they are used. +// They should be replaced with native Set operations, when they are added to the language. +// Proposal reference: https://github.com/tc39/proposal-set-methods + +export const setUnion = (...sets: Set[]): Set => { + const union = new Set(sets[0]); + for (const set of sets.slice(1)) { + for (const element of set) { + union.add(element); + } + } + return union; +}; + +export const setDifference = (setA: Set, ...sets: Set[]): Set => { + const difference = new Set(setA); + for (const set of sets) { + for (const element of set) { + difference.delete(element); + } + } + return difference; +}; + +export const setIsSuperset = (set: Set, subset: Set): boolean => { + for (const element of subset) { + if (!set.has(element)) { + return false; + } + } + return true; +}; + +export const setIsEqual = (setA: Set, setB: Set): boolean => { + return setA.size === setB.size && setIsSuperset(setA, setB); +}; diff --git a/server/src/infra/sql-generator/index.ts b/server/src/utils/sql.ts similarity index 90% rename from server/src/infra/sql-generator/index.ts rename to server/src/utils/sql.ts index 10730fc36..f98273806 100644 --- a/server/src/infra/sql-generator/index.ts +++ b/server/src/utils/sql.ts @@ -5,10 +5,10 @@ import { Test } from '@nestjs/testing'; import { TypeOrmModule } from '@nestjs/typeorm'; import { mkdir, rm, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; +import { format } from 'sql-formatter'; +import { databaseConfig } from 'src/database.config'; import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators'; import { databaseEntities } from 'src/entities'; -import { databaseConfig } from 'src/infra/database.config'; -import { SqlLogger } from 'src/infra/sql-generator/sql.logger'; import { ISystemConfigRepository } from 'src/interfaces/system-config.repository'; import { AccessRepository } from 'src/repositories/access.repository'; import { AlbumRepository } from 'src/repositories/album.repository'; @@ -26,6 +26,30 @@ import { SystemMetadataRepository } from 'src/repositories/system-metadata.repos import { TagRepository } from 'src/repositories/tag.repository'; import { UserTokenRepository } from 'src/repositories/user-token.repository'; import { UserRepository } from 'src/repositories/user.repository'; +import { Logger } from 'typeorm'; + +export class SqlLogger implements Logger { + queries: string[] = []; + errors: Array<{ error: string | Error; query: string }> = []; + + clear() { + this.queries = []; + this.errors = []; + } + + logQuery(query: string) { + this.queries.push(format(query, { language: 'postgresql' })); + } + + logQueryError(error: string | Error, query: string) { + this.errors.push({ error, query }); + } + + logQuerySlow() {} + logSchemaBuild() {} + logMigration() {} + log() {} +} const reflector = new Reflector(); const repositories = [ @@ -178,7 +202,7 @@ class SqlGenerator { } } -new SqlGenerator({ targetDir: './src/infra/sql' }) +new SqlGenerator({ targetDir: './src/queries' }) .run() .then(() => { console.log('Done'); diff --git a/server/src/utils/version.spec.ts b/server/src/utils/version.spec.ts new file mode 100644 index 000000000..34c8abb41 --- /dev/null +++ b/server/src/utils/version.spec.ts @@ -0,0 +1,72 @@ +import { Version, VersionType } from 'src/utils/version'; + +describe('Version', () => { + const tests = [ + { this: new Version(0, 0, 1), other: new Version(0, 0, 0), compare: 1, type: VersionType.PATCH }, + { this: new Version(0, 1, 0), other: new Version(0, 0, 0), compare: 1, type: VersionType.MINOR }, + { this: new Version(1, 0, 0), other: new Version(0, 0, 0), compare: 1, type: VersionType.MAJOR }, + { this: new Version(0, 0, 0), other: new Version(0, 0, 1), compare: -1, type: VersionType.PATCH }, + { this: new Version(0, 0, 0), other: new Version(0, 1, 0), compare: -1, type: VersionType.MINOR }, + { this: new Version(0, 0, 0), other: new Version(1, 0, 0), compare: -1, type: VersionType.MAJOR }, + { this: new Version(0, 0, 0), other: new Version(0, 0, 0), compare: 0, type: VersionType.EQUAL }, + { this: new Version(0, 0, 1), other: new Version(0, 0, 1), compare: 0, type: VersionType.EQUAL }, + { this: new Version(0, 1, 0), other: new Version(0, 1, 0), compare: 0, type: VersionType.EQUAL }, + { this: new Version(1, 0, 0), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, + { this: new Version(1, 0), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, + { this: new Version(1, 0), other: new Version(1, 0, 1), compare: -1, type: VersionType.PATCH }, + { this: new Version(1, 1), other: new Version(1, 0, 1), compare: 1, type: VersionType.MINOR }, + { this: new Version(1), other: new Version(1, 0, 0), compare: 0, type: VersionType.EQUAL }, + { this: new Version(1), other: new Version(1, 0, 1), compare: -1, type: VersionType.PATCH }, + ]; + + describe('isOlderThan', () => { + for (const { this: thisVersion, other: otherVersion, compare, type } of tests) { + const expected = compare < 0 ? type : VersionType.EQUAL; + it(`should return '${expected}' when comparing ${thisVersion} to ${otherVersion}`, () => { + expect(thisVersion.isOlderThan(otherVersion)).toEqual(expected); + }); + } + }); + + describe('isEqual', () => { + for (const { this: thisVersion, other: otherVersion, compare } of tests) { + const bool = compare === 0; + it(`should return ${bool} when comparing ${thisVersion} to ${otherVersion}`, () => { + expect(thisVersion.isEqual(otherVersion)).toEqual(bool); + }); + } + }); + + describe('isNewerThan', () => { + for (const { this: thisVersion, other: otherVersion, compare, type } of tests) { + const expected = compare > 0 ? type : VersionType.EQUAL; + it(`should return ${expected} when comparing ${thisVersion} to ${otherVersion}`, () => { + expect(thisVersion.isNewerThan(otherVersion)).toEqual(expected); + }); + } + }); + + describe('fromString', () => { + const tests = [ + { scenario: 'leading v', value: 'v1.72.2', expected: new Version(1, 72, 2) }, + { scenario: 'uppercase v', value: 'V1.72.2', expected: new Version(1, 72, 2) }, + { scenario: 'missing v', value: '1.72.2', expected: new Version(1, 72, 2) }, + { scenario: 'large patch', value: '1.72.123', expected: new Version(1, 72, 123) }, + { scenario: 'large minor', value: '1.123.0', expected: new Version(1, 123, 0) }, + { scenario: 'large major', value: '123.0.0', expected: new Version(123, 0, 0) }, + { scenario: 'major bump', value: 'v2.0.0', expected: new Version(2, 0, 0) }, + { scenario: 'has dash', value: '14.10-1', expected: new Version(14, 10, 1) }, + { scenario: 'missing patch', value: '14.10', expected: new Version(14, 10, 0) }, + { scenario: 'only major', value: '14', expected: new Version(14, 0, 0) }, + ]; + + for (const { scenario, value, expected } of tests) { + it(`should correctly parse ${scenario}`, () => { + const actual = Version.fromString(value); + expect(actual.major).toEqual(expected.major); + expect(actual.minor).toEqual(expected.minor); + expect(actual.patch).toEqual(expected.patch); + }); + } + }); +}); diff --git a/server/src/utils/version.ts b/server/src/utils/version.ts new file mode 100644 index 000000000..6eca12eb4 --- /dev/null +++ b/server/src/utils/version.ts @@ -0,0 +1,64 @@ +export type IVersion = { major: number; minor: number; patch: number }; + +export enum VersionType { + EQUAL = 0, + PATCH = 1, + MINOR = 2, + MAJOR = 3, +} + +export class Version implements IVersion { + public readonly types = ['major', 'minor', 'patch'] as const; + + constructor( + public major: number, + public minor: number = 0, + public patch: number = 0, + ) {} + + toString() { + return `${this.major}.${this.minor}.${this.patch}`; + } + + toJSON() { + const { major, minor, patch } = this; + return { major, minor, patch }; + } + + static fromString(version: string): Version { + const regex = /v?(?\d+)(?:\.(?\d+))?(?:[.-](?\d+))?/i; + const matchResult = version.match(regex); + if (matchResult) { + const { major, minor = '0', patch = '0' } = matchResult.groups as { [K in keyof IVersion]: string }; + return new Version(Number(major), Number(minor), Number(patch)); + } else { + throw new Error(`Invalid version format: ${version}`); + } + } + + private compare(version: Version): [number, VersionType] { + for (const [i, key] of this.types.entries()) { + const diff = this[key] - version[key]; + if (diff !== 0) { + return [diff > 0 ? 1 : -1, (VersionType.MAJOR - i) as VersionType]; + } + } + + return [0, VersionType.EQUAL]; + } + + isOlderThan(version: Version): VersionType { + const [bool, type] = this.compare(version); + return bool < 0 ? type : VersionType.EQUAL; + } + + isEqual(version: Version): boolean { + const [bool] = this.compare(version); + return bool === 0; + } + + isNewerThan(version: Version): VersionType { + const [bool, type] = this.compare(version); + return bool > 0 ? type : VersionType.EQUAL; + } +} diff --git a/server/test/fixtures/library.stub.ts b/server/test/fixtures/library.stub.ts index b3fa4b7f0..dde250a7a 100644 --- a/server/test/fixtures/library.stub.ts +++ b/server/test/fixtures/library.stub.ts @@ -1,6 +1,6 @@ import { join } from 'node:path'; +import { APP_MEDIA_LOCATION } from 'src/constants'; import { THUMBNAIL_DIR } from 'src/cores/storage.core'; -import { APP_MEDIA_LOCATION } from 'src/domain/domain.constant'; import { LibraryEntity, LibraryType } from 'src/entities/library.entity'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/test/repositories/database.repository.mock.ts b/server/test/repositories/database.repository.mock.ts index 82abe16a8..43a2dfb79 100644 --- a/server/test/repositories/database.repository.mock.ts +++ b/server/test/repositories/database.repository.mock.ts @@ -1,5 +1,5 @@ -import { Version } from 'src/domain/domain.constant'; import { IDatabaseRepository } from 'src/interfaces/database.repository'; +import { Version } from 'src/utils/version'; export const newDatabaseRepositoryMock = (): jest.Mocked => { return { diff --git a/server/test/utils.ts b/server/test/utils.ts index f44f6fe17..4feca9946 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -10,9 +10,8 @@ import { ApiModule } from 'src/apps/api.module'; import { ApiService } from 'src/apps/api.service'; import { AppModule, AppTestModule } from 'src/apps/app.module'; import { MicroservicesService } from 'src/apps/microservices.service'; -import { QueueName } from 'src/domain/job/job.constants'; -import { dataSource } from 'src/infra/database.config'; -import { IJobRepository, JobItem, JobItemHandler } from 'src/interfaces/job.repository'; +import { dataSource } from 'src/database.config'; +import { IJobRepository, JobItem, JobItemHandler, QueueName } from 'src/interfaces/job.repository'; import { IMediaRepository } from 'src/interfaces/media.repository'; import { StorageEventType } from 'src/interfaces/storage.repository'; import { MediaRepository } from 'src/repositories/media.repository'; From 44ed1f091957f061c837767cc1d5dafc36d592e5 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 21 Mar 2024 00:18:38 -0500 Subject: [PATCH 07/33] fix(web): asset-grid padding/margin left fix (#8125) use media query for grid padding/margin size --- web/src/lib/components/photos-page/asset-grid.svelte | 2 +- .../shared-components/navigation-bar/navigation-bar.svelte | 2 +- web/src/routes/(user)/people/[personId]/+page.svelte | 2 +- web/tailwind.config.cjs | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 5892442df..541673764 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -380,7 +380,7 @@
-