Browse Source

refactor(server): domain/infra (#1298)

* refactor: user repository

* refactor: user module

* refactor: move database into infra

* refactor(cli): use user core

* chore: import path

* chore: tests
Jason Rasmussen 2 years ago
parent
commit
131caa20eb
100 changed files with 667 additions and 693 deletions
  1. 8 2
      server/apps/cli/src/app.module.ts
  2. 2 4
      server/apps/cli/src/commands/password-login.ts
  3. 16 18
      server/apps/cli/src/commands/reset-admin-password.command.ts
  4. 1 1
      server/apps/immich/src/api-v1/album/album-repository.ts
  5. 1 3
      server/apps/immich/src/api-v1/album/album.module.ts
  6. 1 1
      server/apps/immich/src/api-v1/album/album.service.spec.ts
  7. 1 1
      server/apps/immich/src/api-v1/album/album.service.ts
  8. 2 2
      server/apps/immich/src/api-v1/album/response-dto/album-response.dto.ts
  9. 1 1
      server/apps/immich/src/api-v1/api-key/api-key.module.ts
  10. 1 1
      server/apps/immich/src/api-v1/api-key/api-key.repository.ts
  11. 1 1
      server/apps/immich/src/api-v1/api-key/api-key.service.ts
  12. 1 1
      server/apps/immich/src/api-v1/api-key/repsonse-dto/api-key-response.dto.ts
  13. 1 1
      server/apps/immich/src/api-v1/asset/asset-repository.ts
  14. 1 3
      server/apps/immich/src/api-v1/asset/asset.module.ts
  15. 1 1
      server/apps/immich/src/api-v1/asset/asset.service.spec.ts
  16. 1 1
      server/apps/immich/src/api-v1/asset/asset.service.ts
  17. 1 1
      server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts
  18. 1 1
      server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts
  19. 1 1
      server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts
  20. 1 1
      server/apps/immich/src/api-v1/asset/response-dto/smart-info-response.dto.ts
  21. 1 1
      server/apps/immich/src/api-v1/auth/auth.controller.ts
  22. 1 2
      server/apps/immich/src/api-v1/auth/auth.module.ts
  23. 3 3
      server/apps/immich/src/api-v1/auth/auth.service.spec.ts
  24. 4 4
      server/apps/immich/src/api-v1/auth/auth.service.ts
  25. 1 1
      server/apps/immich/src/api-v1/auth/response-dto/admin-signup-response.dto.ts
  26. 1 1
      server/apps/immich/src/api-v1/auth/response-dto/login-response.dto.ts
  27. 1 1
      server/apps/immich/src/api-v1/communication/communication.gateway.ts
  28. 1 1
      server/apps/immich/src/api-v1/communication/communication.module.ts
  29. 1 1
      server/apps/immich/src/api-v1/device-info/device-info.module.ts
  30. 1 1
      server/apps/immich/src/api-v1/device-info/device-info.service.spec.ts
  31. 1 1
      server/apps/immich/src/api-v1/device-info/device-info.service.ts
  32. 1 1
      server/apps/immich/src/api-v1/device-info/dto/upsert-device-info.dto.ts
  33. 1 1
      server/apps/immich/src/api-v1/device-info/response-dto/device-info-response.dto.ts
  34. 2 8
      server/apps/immich/src/api-v1/job/job.module.ts
  35. 1 1
      server/apps/immich/src/api-v1/job/job.service.ts
  36. 1 1
      server/apps/immich/src/api-v1/oauth/oauth.controller.ts
  37. 1 2
      server/apps/immich/src/api-v1/oauth/oauth.module.ts
  38. 2 2
      server/apps/immich/src/api-v1/oauth/oauth.service.spec.ts
  39. 2 4
      server/apps/immich/src/api-v1/oauth/oauth.service.ts
  40. 1 1
      server/apps/immich/src/api-v1/server-info/server-info.module.ts
  41. 1 1
      server/apps/immich/src/api-v1/server-info/server-info.service.ts
  42. 2 2
      server/apps/immich/src/api-v1/share/dto/create-shared-link.dto.ts
  43. 1 1
      server/apps/immich/src/api-v1/share/response-dto/shared-link-response.dto.ts
  44. 2 2
      server/apps/immich/src/api-v1/share/share.core.ts
  45. 1 1
      server/apps/immich/src/api-v1/share/share.module.ts
  46. 1 1
      server/apps/immich/src/api-v1/share/shared-link.repository.ts
  47. 1 1
      server/apps/immich/src/api-v1/system-config/dto/system-config.dto.ts
  48. 1 1
      server/apps/immich/src/api-v1/system-config/system-config.module.ts
  49. 1 1
      server/apps/immich/src/api-v1/tag/dto/create-tag.dto.ts
  50. 1 1
      server/apps/immich/src/api-v1/tag/response-dto/tag-response.dto.ts
  51. 1 1
      server/apps/immich/src/api-v1/tag/tag.module.ts
  52. 1 1
      server/apps/immich/src/api-v1/tag/tag.repository.ts
  53. 1 1
      server/apps/immich/src/api-v1/tag/tag.service.spec.ts
  54. 1 1
      server/apps/immich/src/api-v1/tag/tag.service.ts
  55. 0 23
      server/apps/immich/src/api-v1/user/user.module.ts
  56. 11 5
      server/apps/immich/src/app.module.ts
  57. 1 1
      server/apps/immich/src/config/asset-upload.config.ts
  58. 4 1
      server/apps/immich/src/config/profile-image-upload.config.ts
  59. 1 0
      server/apps/immich/src/controllers/index.ts
  60. 11 11
      server/apps/immich/src/controllers/user.controller.ts
  61. 2 10
      server/apps/immich/src/decorators/auth-user.decorator.ts
  62. 2 2
      server/apps/immich/src/global.d.ts
  63. 1 1
      server/apps/immich/src/middlewares/admin-role-guard.middleware.ts
  64. 1 1
      server/apps/immich/src/modules/background-task/background-task.module.ts
  65. 1 1
      server/apps/immich/src/modules/background-task/background-task.processor.ts
  66. 1 1
      server/apps/immich/src/modules/download/download.service.ts
  67. 1 1
      server/apps/immich/src/modules/immich-jwt/immich-jwt.module.ts
  68. 1 1
      server/apps/immich/src/modules/immich-jwt/immich-jwt.service.spec.ts
  69. 1 1
      server/apps/immich/src/modules/immich-jwt/immich-jwt.service.ts
  70. 1 1
      server/apps/immich/src/modules/immich-jwt/strategies/jwt.strategy.ts
  71. 1 1
      server/apps/immich/src/modules/immich-jwt/strategies/public-share.strategy.ts
  72. 1 1
      server/apps/immich/src/modules/schedule-tasks/schedule-tasks.module.ts
  73. 1 1
      server/apps/immich/src/modules/schedule-tasks/schedule-tasks.service.ts
  74. 3 4
      server/apps/immich/test/album.e2e-spec.ts
  75. 4 8
      server/apps/immich/test/jest-e2e.json
  76. 0 15
      server/apps/immich/test/test-utils.ts
  77. 4 7
      server/apps/immich/test/user.e2e-spec.ts
  78. 5 2
      server/apps/microservices/src/microservices.module.ts
  79. 1 1
      server/apps/microservices/src/processors/asset-uploaded.processor.ts
  80. 1 1
      server/apps/microservices/src/processors/generate-checksum.processor.ts
  81. 2 2
      server/apps/microservices/src/processors/machine-learning.processor.ts
  82. 1 1
      server/apps/microservices/src/processors/metadata-extraction.processor.ts
  83. 1 1
      server/apps/microservices/src/processors/storage-migration.processor.ts
  84. 1 1
      server/apps/microservices/src/processors/thumbnail.processor.ts
  85. 1 1
      server/apps/microservices/src/processors/user-deletion.processor.ts
  86. 1 1
      server/apps/microservices/src/processors/video-transcode.processor.ts
  87. 471 471
      server/immich-openapi-specs.json
  88. 1 1
      server/libs/common/src/utils/asset-utils.ts
  89. 1 1
      server/libs/common/src/utils/user-utils.spec.ts
  90. 1 1
      server/libs/common/src/utils/user-utils.ts
  91. 0 10
      server/libs/database/src/database.module.ts
  92. 8 0
      server/libs/domain/src/auth/dto/auth-user.dto.ts
  93. 1 0
      server/libs/domain/src/auth/dto/index.ts
  94. 1 0
      server/libs/domain/src/auth/index.ts
  95. 20 0
      server/libs/domain/src/domain.module.ts
  96. 3 0
      server/libs/domain/src/index.ts
  97. 0 0
      server/libs/domain/src/user/dto/create-profile-image.dto.ts
  98. 0 0
      server/libs/domain/src/user/dto/create-user.dto.spec.ts
  99. 0 0
      server/libs/domain/src/user/dto/create-user.dto.ts
  100. 4 0
      server/libs/domain/src/user/dto/index.ts

+ 8 - 2
server/apps/cli/src/app.module.ts

@@ -1,11 +1,17 @@
-import { DatabaseModule, SystemConfigEntity, UserEntity } from '@app/database';
+import { DomainModule } from '@app/domain';
+import { InfraModule, SystemConfigEntity } from '@app/infra';
 import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from './commands/password-login';
 import { PromptPasswordQuestions, ResetAdminPasswordCommand } from './commands/reset-admin-password.command';
 
 @Module({
-  imports: [DatabaseModule, TypeOrmModule.forFeature([UserEntity, SystemConfigEntity])],
+  imports: [
+    DomainModule.register({
+      imports: [InfraModule],
+    }),
+    TypeOrmModule.forFeature([SystemConfigEntity]),
+  ],
   providers: [
     ResetAdminPasswordCommand,
     PromptPasswordQuestions,

+ 2 - 4
server/apps/cli/src/commands/password-login.ts

@@ -1,4 +1,4 @@
-import { SystemConfigEntity, SystemConfigKey } from '@app/database';
+import { SystemConfigEntity, SystemConfigKey } from '@app/infra';
 import { InjectRepository } from '@nestjs/typeorm';
 import axios from 'axios';
 import { Command, CommandRunner } from 'nest-commander';
@@ -9,9 +9,7 @@ import { Repository } from 'typeorm';
   description: 'Enable password login',
 })
 export class EnablePasswordLoginCommand extends CommandRunner {
-  constructor(
-    @InjectRepository(SystemConfigEntity) private repository: Repository<SystemConfigEntity>, //
-  ) {
+  constructor(@InjectRepository(SystemConfigEntity) private repository: Repository<SystemConfigEntity>) {
     super();
   }
 

+ 16 - 18
server/apps/cli/src/commands/reset-admin-password.command.ts

@@ -1,40 +1,38 @@
-import { UserEntity } from '@app/database';
-import { InjectRepository } from '@nestjs/typeorm';
-import bcrypt from 'bcrypt';
+import { Inject } from '@nestjs/common';
 import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander';
 import { randomBytes } from 'node:crypto';
-import { Repository } from 'typeorm';
+import { IUserRepository, UserCore } from '@app/domain';
 
 @Command({
   name: 'reset-admin-password',
   description: 'Reset the admin password',
 })
 export class ResetAdminPasswordCommand extends CommandRunner {
-  constructor(
-    private readonly inquirer: InquirerService,
-    @InjectRepository(UserEntity) private userRepository: Repository<UserEntity>,
-  ) {
+  userCore: UserCore;
+
+  constructor(private readonly inquirer: InquirerService, @Inject(IUserRepository) userRepository: IUserRepository) {
     super();
+
+    this.userCore = new UserCore(userRepository);
   }
 
   async run(): Promise<void> {
-    let { password } = await this.inquirer.ask<{ password: string }>('prompt-password', undefined);
-    password = password || randomBytes(24).toString('base64').replace(/\W/g, '');
-
-    const hashedPassword = await bcrypt.hash(password, 10);
-
-    const user = await this.userRepository.findOne({ where: { isAdmin: true } });
+    const user = await this.userCore.getAdmin();
     if (!user) {
       console.log('Unable to reset password: no admin user.');
       return;
     }
 
-    user.password = hashedPassword;
-    user.shouldChangePassword = true;
+    const { password: providedPassword } = await this.inquirer.ask<{ password: string }>('prompt-password', undefined);
+    const password = providedPassword || randomBytes(24).toString('base64').replace(/\W/g, '');
 
-    await this.userRepository.save(user);
+    await this.userCore.updateUser(user, user.id, { password });
 
-    console.log(`New password:\n${password}`);
+    if (providedPassword) {
+      console.log('The admin password has been updated.');
+    } else {
+      console.log(`The admin password has been updated to:\n${password}`);
+    }
   }
 }
 

+ 1 - 1
server/apps/immich/src/api-v1/album/album-repository.ts

@@ -1,4 +1,4 @@
-import { AlbumEntity, AssetAlbumEntity, UserAlbumEntity } from '@app/database';
+import { AlbumEntity, AssetAlbumEntity, UserAlbumEntity } from '@app/infra';
 import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { In, Repository, SelectQueryBuilder, DataSource, Brackets, Not, IsNull } from 'typeorm';

+ 1 - 3
server/apps/immich/src/api-v1/album/album.module.ts

@@ -2,11 +2,10 @@ import { forwardRef, Module } from '@nestjs/common';
 import { AlbumService } from './album.service';
 import { AlbumController } from './album.controller';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { AlbumEntity, AssetAlbumEntity, UserAlbumEntity } from '@app/database';
+import { AlbumEntity, AssetAlbumEntity, UserAlbumEntity } from '@app/infra';
 import { AlbumRepository, IAlbumRepository } from './album-repository';
 import { DownloadModule } from '../../modules/download/download.module';
 import { AssetModule } from '../asset/asset.module';
-import { UserModule } from '../user/user.module';
 import { ShareModule } from '../share/share.module';
 
 const ALBUM_REPOSITORY_PROVIDER = {
@@ -18,7 +17,6 @@ const ALBUM_REPOSITORY_PROVIDER = {
   imports: [
     TypeOrmModule.forFeature([AlbumEntity, AssetAlbumEntity, UserAlbumEntity]),
     DownloadModule,
-    UserModule,
     forwardRef(() => AssetModule),
     ShareModule,
   ],

+ 1 - 1
server/apps/immich/src/api-v1/album/album.service.spec.ts

@@ -1,7 +1,7 @@
 import { AlbumService } from './album.service';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common';
-import { AlbumEntity } from '@app/database';
+import { AlbumEntity } from '@app/infra';
 import { AlbumResponseDto } from './response-dto/album-response.dto';
 import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
 import { IAlbumRepository } from './album-repository';

+ 1 - 1
server/apps/immich/src/api-v1/album/album.service.ts

@@ -1,7 +1,7 @@
 import { BadRequestException, Inject, Injectable, NotFoundException, ForbiddenException, Logger } from '@nestjs/common';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { CreateAlbumDto } from './dto/create-album.dto';
-import { AlbumEntity, SharedLinkType } from '@app/database';
+import { AlbumEntity, SharedLinkType } from '@app/infra';
 import { AddUsersDto } from './dto/add-users.dto';
 import { RemoveAssetsDto } from './dto/remove-assets.dto';
 import { UpdateAlbumDto } from './dto/update-album.dto';

+ 2 - 2
server/apps/immich/src/api-v1/album/response-dto/album-response.dto.ts

@@ -1,5 +1,5 @@
-import { AlbumEntity } from '@app/database';
-import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto';
+import { AlbumEntity } from '@app/infra';
+import { UserResponseDto, mapUser } from '@app/domain';
 import { AssetResponseDto, mapAsset } from '../../asset/response-dto/asset-response.dto';
 import { ApiProperty } from '@nestjs/swagger';
 

+ 1 - 1
server/apps/immich/src/api-v1/api-key/api-key.module.ts

@@ -1,4 +1,4 @@
-import { APIKeyEntity } from '@app/database';
+import { APIKeyEntity } from '@app/infra';
 import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { APIKeyController } from './api-key.controller';

+ 1 - 1
server/apps/immich/src/api-v1/api-key/api-key.repository.ts

@@ -1,4 +1,4 @@
-import { APIKeyEntity } from '@app/database';
+import { APIKeyEntity } from '@app/infra';
 import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';

+ 1 - 1
server/apps/immich/src/api-v1/api-key/api-key.service.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { BadRequestException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
 import { compareSync, hash } from 'bcrypt';
 import { randomBytes } from 'node:crypto';

+ 1 - 1
server/apps/immich/src/api-v1/api-key/repsonse-dto/api-key-response.dto.ts

@@ -1,4 +1,4 @@
-import { APIKeyEntity } from '@app/database';
+import { APIKeyEntity } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class APIKeyResponseDto {

+ 1 - 1
server/apps/immich/src/api-v1/asset/asset-repository.ts

@@ -1,6 +1,6 @@
 import { SearchPropertiesDto } from './dto/search-properties.dto';
 import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
-import { AssetEntity, AssetType } from '@app/database';
+import { AssetEntity, AssetType } from '@app/infra';
 import { BadRequestException, Inject, Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm/repository/Repository';

+ 1 - 3
server/apps/immich/src/api-v1/asset/asset.module.ts

@@ -2,7 +2,7 @@ import { forwardRef, Module } from '@nestjs/common';
 import { AssetService } from './asset.service';
 import { AssetController } from './asset.controller';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { BullModule } from '@nestjs/bull';
 import { BackgroundTaskModule } from '../../modules/background-task/background-task.module';
 import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
@@ -11,7 +11,6 @@ import { AssetRepository, IAssetRepository } from './asset-repository';
 import { DownloadModule } from '../../modules/download/download.module';
 import { TagModule } from '../tag/tag.module';
 import { AlbumModule } from '../album/album.module';
-import { UserModule } from '../user/user.module';
 import { StorageModule } from '@app/storage';
 import { immichSharedQueues } from '@app/job/constants/bull-queue-registration.constant';
 import { ShareModule } from '../share/share.module';
@@ -27,7 +26,6 @@ const ASSET_REPOSITORY_PROVIDER = {
     CommunicationModule,
     BackgroundTaskModule,
     DownloadModule,
-    UserModule,
     AlbumModule,
     TagModule,
     StorageModule,

+ 1 - 1
server/apps/immich/src/api-v1/asset/asset.service.spec.ts

@@ -2,7 +2,7 @@ import { IAssetRepository } from './asset-repository';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { AssetService } from './asset.service';
 import { Repository } from 'typeorm';
-import { AssetEntity, AssetType } from '@app/database';
+import { AssetEntity, AssetType } from '@app/infra';
 import { CreateAssetDto } from './dto/create-asset.dto';
 import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto';
 import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';

+ 1 - 1
server/apps/immich/src/api-v1/asset/asset.service.ts

@@ -13,7 +13,7 @@ import { InjectRepository } from '@nestjs/typeorm';
 import { createHash, randomUUID } from 'node:crypto';
 import { QueryFailedError, Repository } from 'typeorm';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
-import { AssetEntity, AssetType } from '@app/database';
+import { AssetEntity, AssetType } from '@app/infra';
 import { constants, createReadStream, ReadStream, stat } from 'fs';
 import { ServeFileDto } from './dto/serve-file.dto';
 import { Response as Res } from 'express';

+ 1 - 1
server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts

@@ -1,5 +1,5 @@
 import { IsNotEmpty, IsOptional } from 'class-validator';
-import { AssetType } from '@app/database';
+import { AssetType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class CreateAssetDto {

+ 1 - 1
server/apps/immich/src/api-v1/asset/response-dto/asset-response.dto.ts

@@ -1,4 +1,4 @@
-import { AssetEntity, AssetType } from '@app/database';
+import { AssetEntity, AssetType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 import { mapTag, TagResponseDto } from '../../tag/response-dto/tag-response.dto';
 import { ExifResponseDto, mapExif } from './exif-response.dto';

+ 1 - 1
server/apps/immich/src/api-v1/asset/response-dto/exif-response.dto.ts

@@ -1,4 +1,4 @@
-import { ExifEntity } from '@app/database';
+import { ExifEntity } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class ExifResponseDto {

+ 1 - 1
server/apps/immich/src/api-v1/asset/response-dto/smart-info-response.dto.ts

@@ -1,4 +1,4 @@
-import { SmartInfoEntity } from '@app/database';
+import { SmartInfoEntity } from '@app/infra';
 
 export class SmartInfoResponseDto {
   id?: string;

+ 1 - 1
server/apps/immich/src/api-v1/auth/auth.controller.ts

@@ -5,7 +5,7 @@ import { AuthType, IMMICH_AUTH_TYPE_COOKIE } from '../../constants/jwt.constant'
 import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 import { Authenticated } from '../../decorators/authenticated.decorator';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
-import { UserResponseDto } from '../user/response-dto/user-response.dto';
+import { UserResponseDto } from '@app/domain';
 import { AuthService } from './auth.service';
 import { ChangePasswordDto } from './dto/change-password.dto';
 import { LoginCredentialDto } from './dto/login-credential.dto';

+ 1 - 2
server/apps/immich/src/api-v1/auth/auth.module.ts

@@ -2,12 +2,11 @@ import { Module } from '@nestjs/common';
 import { ImmichConfigModule } from '@app/immich-config';
 import { ImmichJwtModule } from '../../modules/immich-jwt/immich-jwt.module';
 import { OAuthModule } from '../oauth/oauth.module';
-import { UserModule } from '../user/user.module';
 import { AuthController } from './auth.controller';
 import { AuthService } from './auth.service';
 
 @Module({
-  imports: [UserModule, ImmichJwtModule, OAuthModule, ImmichConfigModule],
+  imports: [ImmichJwtModule, OAuthModule, ImmichConfigModule],
   controllers: [AuthController],
   providers: [AuthService],
 })

+ 3 - 3
server/apps/immich/src/api-v1/auth/auth.service.spec.ts

@@ -1,12 +1,12 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { BadRequestException, UnauthorizedException } from '@nestjs/common';
 import * as bcrypt from 'bcrypt';
-import { SystemConfig } from '@app/database/entities/system-config.entity';
+import { SystemConfig } from '@app/infra';
 import { ImmichConfigService } from '@app/immich-config';
 import { AuthType } from '../../constants/jwt.constant';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { OAuthService } from '../oauth/oauth.service';
-import { IUserRepository } from '../user/user-repository';
+import { IUserRepository } from '@app/domain';
 import { AuthService } from './auth.service';
 import { SignUpDto } from './dto/sign-up.dto';
 import { LoginResponseDto } from './response-dto/login-response.dto';

+ 4 - 4
server/apps/immich/src/api-v1/auth/auth.service.ts

@@ -7,11 +7,11 @@ import {
   UnauthorizedException,
 } from '@nestjs/common';
 import * as bcrypt from 'bcrypt';
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { AuthType } from '../../constants/jwt.constant';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
-import { IUserRepository } from '../user/user-repository';
+import { IUserRepository } from '@app/domain';
 import { ChangePasswordDto } from './dto/change-password.dto';
 import { LoginCredentialDto } from './dto/login-credential.dto';
 import { SignUpDto } from './dto/sign-up.dto';
@@ -19,9 +19,9 @@ import { AdminSignupResponseDto, mapAdminSignupResponse } from './response-dto/a
 import { LoginResponseDto } from './response-dto/login-response.dto';
 import { LogoutResponseDto } from './response-dto/logout-response.dto';
 import { OAuthService } from '../oauth/oauth.service';
-import { UserCore } from '../user/user.core';
+import { UserCore } from '@app/domain';
 import { ImmichConfigService, INITIAL_SYSTEM_CONFIG } from '@app/immich-config';
-import { SystemConfig } from '@app/database/entities/system-config.entity';
+import { SystemConfig } from '@app/infra';
 
 @Injectable()
 export class AuthService {

+ 1 - 1
server/apps/immich/src/api-v1/auth/response-dto/admin-signup-response.dto.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 
 export class AdminSignupResponseDto {
   id!: string;

+ 1 - 1
server/apps/immich/src/api-v1/auth/response-dto/login-response.dto.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { ApiResponseProperty } from '@nestjs/swagger';
 
 export class LoginResponseDto {

+ 1 - 1
server/apps/immich/src/api-v1/communication/communication.gateway.ts

@@ -3,7 +3,7 @@ import { Socket, Server } from 'socket.io';
 import { ImmichJwtService, JwtValidationResult } from '../../modules/immich-jwt/immich-jwt.service';
 import { Logger } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { Repository } from 'typeorm';
 import cookieParser from 'cookie';
 import { IMMICH_ACCESS_COOKIE } from '../../constants/jwt.constant';

+ 1 - 1
server/apps/immich/src/api-v1/communication/communication.module.ts

@@ -6,7 +6,7 @@ import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { JwtModule } from '@nestjs/jwt';
 import { jwtConfig } from '../../config/jwt.config';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 
 @Module({
   imports: [TypeOrmModule.forFeature([UserEntity]), ImmichJwtModule, JwtModule.register(jwtConfig)],

+ 1 - 1
server/apps/immich/src/api-v1/device-info/device-info.module.ts

@@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
 import { DeviceInfoService } from './device-info.service';
 import { DeviceInfoController } from './device-info.controller';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { DeviceInfoEntity } from '@app/database';
+import { DeviceInfoEntity } from '@app/infra';
 
 @Module({
   imports: [TypeOrmModule.forFeature([DeviceInfoEntity])],

+ 1 - 1
server/apps/immich/src/api-v1/device-info/device-info.service.spec.ts

@@ -1,4 +1,4 @@
-import { DeviceInfoEntity, DeviceType } from '@app/database';
+import { DeviceInfoEntity, DeviceType } from '@app/infra';
 import { Repository } from 'typeorm';
 import { DeviceInfoService } from './device-info.service';
 

+ 1 - 1
server/apps/immich/src/api-v1/device-info/device-info.service.ts

@@ -1,4 +1,4 @@
-import { DeviceInfoEntity } from '@app/database';
+import { DeviceInfoEntity } from '@app/infra';
 import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';

+ 1 - 1
server/apps/immich/src/api-v1/device-info/dto/upsert-device-info.dto.ts

@@ -1,5 +1,5 @@
 import { IsNotEmpty, IsOptional } from 'class-validator';
-import { DeviceType } from '@app/database';
+import { DeviceType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class UpsertDeviceInfoDto {

+ 1 - 1
server/apps/immich/src/api-v1/device-info/response-dto/device-info-response.dto.ts

@@ -1,4 +1,4 @@
-import { DeviceInfoEntity, DeviceType } from '@app/database';
+import { DeviceInfoEntity, DeviceType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class DeviceInfoResponseDto {

+ 2 - 8
server/apps/immich/src/api-v1/job/job.module.ts

@@ -1,15 +1,11 @@
 import { Module } from '@nestjs/common';
 import { JobService } from './job.service';
 import { JobController } from './job.controller';
-import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { ImmichJwtModule } from '../../modules/immich-jwt/immich-jwt.module';
-import { JwtModule } from '@nestjs/jwt';
-import { jwtConfig } from '../../config/jwt.config';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { ExifEntity } from '@app/database';
+import { ExifEntity } from '@app/infra';
 import { TagModule } from '../tag/tag.module';
 import { AssetModule } from '../asset/asset.module';
-import { UserModule } from '../user/user.module';
 
 import { StorageModule } from '@app/storage';
 import { BullModule } from '@nestjs/bull';
@@ -21,12 +17,10 @@ import { immichSharedQueues } from '@app/job/constants/bull-queue-registration.c
     ImmichJwtModule,
     TagModule,
     AssetModule,
-    UserModule,
-    JwtModule.register(jwtConfig),
     StorageModule,
     BullModule.registerQueue(...immichSharedQueues),
   ],
   controllers: [JobController],
-  providers: [JobService, ImmichJwtService],
+  providers: [JobService],
 })
 export class JobModule {}

+ 1 - 1
server/apps/immich/src/api-v1/job/job.service.ts

@@ -15,7 +15,7 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common';
 import { AllJobStatusResponseDto } from './response-dto/all-job-status-response.dto';
 import { randomUUID } from 'crypto';
 import { IAssetRepository } from '../asset/asset-repository';
-import { AssetType } from '@app/database';
+import { AssetType } from '@app/infra';
 import { GetJobDto, JobId } from './dto/get-job.dto';
 import { JobStatusResponseDto } from './response-dto/job-status-response.dto';
 import { IMachineLearningJob } from '@app/job/interfaces/machine-learning.interface';

+ 1 - 1
server/apps/immich/src/api-v1/oauth/oauth.controller.ts

@@ -6,7 +6,7 @@ import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
 import { Authenticated } from '../../decorators/authenticated.decorator';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { LoginResponseDto } from '../auth/response-dto/login-response.dto';
-import { UserResponseDto } from '../user/response-dto/user-response.dto';
+import { UserResponseDto } from '@app/domain';
 import { OAuthCallbackDto } from './dto/oauth-auth-code.dto';
 import { OAuthConfigDto } from './dto/oauth-config.dto';
 import { MOBILE_REDIRECT, OAuthService } from './oauth.service';

+ 1 - 2
server/apps/immich/src/api-v1/oauth/oauth.module.ts

@@ -1,12 +1,11 @@
 import { ImmichConfigModule } from '@app/immich-config';
 import { Module } from '@nestjs/common';
 import { ImmichJwtModule } from '../../modules/immich-jwt/immich-jwt.module';
-import { UserModule } from '../user/user.module';
 import { OAuthController } from './oauth.controller';
 import { OAuthService } from './oauth.service';
 
 @Module({
-  imports: [UserModule, ImmichJwtModule, ImmichConfigModule],
+  imports: [ImmichJwtModule, ImmichConfigModule],
   controllers: [OAuthController],
   providers: [OAuthService],
   exports: [OAuthService],

+ 2 - 2
server/apps/immich/src/api-v1/oauth/oauth.service.spec.ts

@@ -1,4 +1,4 @@
-import { SystemConfig, UserEntity } from '@app/database';
+import { SystemConfig, UserEntity } from '@app/infra';
 import { ImmichConfigService } from '@app/immich-config';
 import { BadRequestException } from '@nestjs/common';
 import { generators, Issuer } from 'openid-client';
@@ -6,7 +6,7 @@ import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { LoginResponseDto } from '../auth/response-dto/login-response.dto';
 import { OAuthService } from '../oauth/oauth.service';
-import { IUserRepository } from '../user/user-repository';
+import { IUserRepository } from '@app/domain';
 
 const email = 'user@immich.com';
 const sub = 'my-auth-user-sub';

+ 2 - 4
server/apps/immich/src/api-v1/oauth/oauth.service.ts

@@ -1,13 +1,11 @@
-import { SystemConfig } from '@app/database/entities/system-config.entity';
+import { SystemConfig } from '@app/infra';
 import { ImmichConfigService, INITIAL_SYSTEM_CONFIG } from '@app/immich-config';
 import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
 import { ClientMetadata, custom, generators, Issuer, UserinfoResponse } from 'openid-client';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
 import { LoginResponseDto } from '../auth/response-dto/login-response.dto';
-import { UserResponseDto } from '../user/response-dto/user-response.dto';
-import { IUserRepository } from '../user/user-repository';
-import { UserCore } from '../user/user.core';
+import { IUserRepository, UserResponseDto, UserCore } from '@app/domain';
 import { OAuthCallbackDto } from './dto/oauth-auth-code.dto';
 import { OAuthConfigDto } from './dto/oauth-config.dto';
 import { OAuthConfigResponseDto } from './response-dto/oauth-config-response.dto';

+ 1 - 1
server/apps/immich/src/api-v1/server-info/server-info.module.ts

@@ -1,7 +1,7 @@
 import { Module } from '@nestjs/common';
 import { ServerInfoService } from './server-info.service';
 import { ServerInfoController } from './server-info.controller';
-import { AssetEntity, UserEntity } from '@app/database';
+import { AssetEntity, UserEntity } from '@app/infra';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { ImmichJwtModule } from '../../modules/immich-jwt/immich-jwt.module';
 

+ 1 - 1
server/apps/immich/src/api-v1/server-info/server-info.service.ts

@@ -4,7 +4,7 @@ import { ServerInfoResponseDto } from './response-dto/server-info-response.dto';
 import diskusage from 'diskusage';
 import { ServerStatsResponseDto } from './response-dto/server-stats-response.dto';
 import { UsageByUserDto } from './response-dto/usage-by-user-response.dto';
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { Repository } from 'typeorm';
 import { InjectRepository } from '@nestjs/typeorm';
 import { asHumanReadable } from '../../utils/human-readable.util';

+ 2 - 2
server/apps/immich/src/api-v1/share/dto/create-shared-link.dto.ts

@@ -1,5 +1,5 @@
-import { AlbumEntity, AssetEntity } from '@app/database';
-import { SharedLinkType } from '@app/database/entities/shared-link.entity';
+import { AlbumEntity, AssetEntity } from '@app/infra';
+import { SharedLinkType } from '@app/infra';
 
 export class CreateSharedLinkDto {
   description?: string;

+ 1 - 1
server/apps/immich/src/api-v1/share/response-dto/shared-link-response.dto.ts

@@ -1,4 +1,4 @@
-import { SharedLinkEntity, SharedLinkType } from '@app/database';
+import { SharedLinkEntity, SharedLinkType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 import _ from 'lodash';
 import { AlbumResponseDto, mapAlbumExcludeAssetInfo } from '../../album/response-dto/album-response.dto';

+ 2 - 2
server/apps/immich/src/api-v1/share/share.core.ts

@@ -1,9 +1,9 @@
-import { SharedLinkEntity } from '@app/database/entities/shared-link.entity';
+import { SharedLinkEntity } from '@app/infra';
 import { CreateSharedLinkDto } from './dto/create-shared-link.dto';
 import { ISharedLinkRepository } from './shared-link.repository';
 import crypto from 'node:crypto';
 import { BadRequestException, InternalServerErrorException, Logger } from '@nestjs/common';
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { EditSharedLinkDto } from './dto/edit-shared-link.dto';
 
 export class ShareCore {

+ 1 - 1
server/apps/immich/src/api-v1/share/share.module.ts

@@ -1,7 +1,7 @@
 import { Module } from '@nestjs/common';
 import { ShareService } from './share.service';
 import { ShareController } from './share.controller';
-import { SharedLinkEntity } from '@app/database/entities/shared-link.entity';
+import { SharedLinkEntity } from '@app/infra';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { SharedLinkRepository, ISharedLinkRepository } from './shared-link.repository';
 

+ 1 - 1
server/apps/immich/src/api-v1/share/shared-link.repository.ts

@@ -1,4 +1,4 @@
-import { SharedLinkEntity } from '@app/database/entities/shared-link.entity';
+import { SharedLinkEntity } from '@app/infra';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
 

+ 1 - 1
server/apps/immich/src/api-v1/system-config/dto/system-config.dto.ts

@@ -1,4 +1,4 @@
-import { SystemConfig } from '@app/database';
+import { SystemConfig } from '@app/infra';
 import { ValidateNested } from 'class-validator';
 import { SystemConfigFFmpegDto } from './system-config-ffmpeg.dto';
 import { SystemConfigOAuthDto } from './system-config-oauth.dto';

+ 1 - 1
server/apps/immich/src/api-v1/system-config/system-config.module.ts

@@ -1,4 +1,4 @@
-import { SystemConfigEntity } from '@app/database';
+import { SystemConfigEntity } from '@app/infra';
 import { immichSharedQueues } from '@app/job/constants/bull-queue-registration.constant';
 import { BullModule } from '@nestjs/bull';
 import { Module } from '@nestjs/common';

+ 1 - 1
server/apps/immich/src/api-v1/tag/dto/create-tag.dto.ts

@@ -1,4 +1,4 @@
-import { TagType } from '@app/database';
+import { TagType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
 

+ 1 - 1
server/apps/immich/src/api-v1/tag/response-dto/tag-response.dto.ts

@@ -1,4 +1,4 @@
-import { TagEntity, TagType } from '@app/database';
+import { TagEntity, TagType } from '@app/infra';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class TagResponseDto {

+ 1 - 1
server/apps/immich/src/api-v1/tag/tag.module.ts

@@ -1,7 +1,7 @@
 import { Module } from '@nestjs/common';
 import { TagService } from './tag.service';
 import { TagController } from './tag.controller';
-import { TagEntity } from '@app/database';
+import { TagEntity } from '@app/infra';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { TagRepository, ITagRepository } from './tag.repository';
 

+ 1 - 1
server/apps/immich/src/api-v1/tag/tag.repository.ts

@@ -1,4 +1,4 @@
-import { TagEntity, TagType } from '@app/database';
+import { TagEntity, TagType } from '@app/infra';
 import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { In, Repository } from 'typeorm';

+ 1 - 1
server/apps/immich/src/api-v1/tag/tag.service.spec.ts

@@ -1,4 +1,4 @@
-import { TagEntity, TagType, UserEntity } from '@app/database';
+import { TagEntity, TagType, UserEntity } from '@app/infra';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { ITagRepository } from './tag.repository';
 import { TagService } from './tag.service';

+ 1 - 1
server/apps/immich/src/api-v1/tag/tag.service.ts

@@ -1,4 +1,4 @@
-import { TagEntity } from '@app/database';
+import { TagEntity } from '@app/infra';
 import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { CreateTagDto } from './dto/create-tag.dto';

+ 0 - 23
server/apps/immich/src/api-v1/user/user.module.ts

@@ -1,23 +0,0 @@
-import { UserEntity } from '@app/database';
-import { Module } from '@nestjs/common';
-import { JwtModule } from '@nestjs/jwt';
-import { TypeOrmModule } from '@nestjs/typeorm';
-import { jwtConfig } from '../../config/jwt.config';
-import { ImmichJwtModule } from '../../modules/immich-jwt/immich-jwt.module';
-import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
-import { UserRepository, IUserRepository } from './user-repository';
-import { UserController } from './user.controller';
-import { UserService } from './user.service';
-
-const USER_REPOSITORY_PROVIDER = {
-  provide: IUserRepository,
-  useClass: UserRepository,
-};
-
-@Module({
-  imports: [TypeOrmModule.forFeature([UserEntity]), ImmichJwtModule, JwtModule.register(jwtConfig)],
-  controllers: [UserController],
-  providers: [UserService, ImmichJwtService, USER_REPOSITORY_PROVIDER],
-  exports: [USER_REPOSITORY_PROVIDER],
-})
-export class UserModule {}

+ 11 - 5
server/apps/immich/src/app.module.ts

@@ -1,6 +1,5 @@
 import { immichAppConfig, immichBullAsyncConfig } from '@app/common/config';
 import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
-import { UserModule } from './api-v1/user/user.module';
 import { AssetModule } from './api-v1/asset/asset.module';
 import { AuthModule } from './api-v1/auth/auth.module';
 import { APIKeyModule } from './api-v1/api-key/api-key.module';
@@ -15,20 +14,23 @@ import { AlbumModule } from './api-v1/album/album.module';
 import { AppController } from './app.controller';
 import { ScheduleModule } from '@nestjs/schedule';
 import { ScheduleTasksModule } from './modules/schedule-tasks/schedule-tasks.module';
-import { DatabaseModule } from '@app/database';
 import { JobModule } from './api-v1/job/job.module';
 import { SystemConfigModule } from './api-v1/system-config/system-config.module';
 import { OAuthModule } from './api-v1/oauth/oauth.module';
 import { TagModule } from './api-v1/tag/tag.module';
 import { ImmichConfigModule } from '@app/immich-config';
 import { ShareModule } from './api-v1/share/share.module';
+import { DomainModule } from '@app/domain';
+import { InfraModule } from '@app/infra';
+import { UserController } from './controllers';
 
 @Module({
   imports: [
     ConfigModule.forRoot(immichAppConfig),
 
-    DatabaseModule,
-    UserModule,
+    DomainModule.register({
+      imports: [InfraModule],
+    }),
 
     APIKeyModule,
 
@@ -64,7 +66,11 @@ import { ShareModule } from './api-v1/share/share.module';
 
     ShareModule,
   ],
-  controllers: [AppController],
+  controllers: [
+    //
+    AppController,
+    UserController,
+  ],
   providers: [],
 })
 export class AppModule implements NestModule {

+ 1 - 1
server/apps/immich/src/config/asset-upload.config.ts

@@ -51,7 +51,7 @@ function destination(req: Request, file: Express.Multer.File, cb: any) {
 
   const basePath = APP_UPLOAD_LOCATION;
   const sanitizedDeviceId = sanitize(String(req.body['deviceId']));
-  const originalUploadFolder = join(basePath, req.user.id, 'original', sanitizedDeviceId);
+  const originalUploadFolder = join(basePath, user.id, 'original', sanitizedDeviceId);
 
   if (!existsSync(originalUploadFolder)) {
     mkdirSync(originalUploadFolder, { recursive: true });

+ 4 - 1
server/apps/immich/src/config/profile-image-upload.config.ts

@@ -6,6 +6,7 @@ import { existsSync, mkdirSync } from 'fs';
 import { diskStorage } from 'multer';
 import { extname } from 'path';
 import sanitize from 'sanitize-filename';
+import { AuthUserDto } from '../decorators/auth-user.decorator';
 import { patchFormData } from '../utils/path-form-data.util';
 
 export const profileImageUploadOption: MulterOptions = {
@@ -35,8 +36,10 @@ function destination(req: Request, file: Express.Multer.File, cb: any) {
     return cb(new UnauthorizedException());
   }
 
+  const user = req.user as AuthUserDto;
+
   const basePath = APP_UPLOAD_LOCATION;
-  const profileImageLocation = `${basePath}/${req.user.id}/profile`;
+  const profileImageLocation = `${basePath}/${user.id}/profile`;
 
   if (!existsSync(profileImageLocation)) {
     mkdirSync(profileImageLocation, { recursive: true });

+ 1 - 0
server/apps/immich/src/controllers/index.ts

@@ -0,0 +1 @@
+export * from './user.controller';

+ 11 - 11
server/apps/immich/src/api-v1/user/user.controller.ts → server/apps/immich/src/controllers/user.controller.ts

@@ -15,20 +15,20 @@ import {
   StreamableFile,
   Header,
 } from '@nestjs/common';
-import { UserService } from './user.service';
-import { Authenticated } from '../../decorators/authenticated.decorator';
-import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
-import { CreateUserDto } from './dto/create-user.dto';
-import { UpdateUserDto } from './dto/update-user.dto';
+import { UserService } from '@app/domain';
+import { Authenticated } from '../decorators/authenticated.decorator';
+import { AuthUserDto, GetAuthUser } from '../decorators/auth-user.decorator';
+import { CreateUserDto } from '@app/domain';
+import { UpdateUserDto } from '@app/domain';
 import { FileInterceptor } from '@nestjs/platform-express';
-import { profileImageUploadOption } from '../../config/profile-image-upload.config';
+import { profileImageUploadOption } from '../config/profile-image-upload.config';
 import { Response as Res } from 'express';
 import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
-import { UserResponseDto } from './response-dto/user-response.dto';
-import { UserCountResponseDto } from './response-dto/user-count-response.dto';
-import { CreateProfileImageDto } from './dto/create-profile-image.dto';
-import { CreateProfileImageResponseDto } from './response-dto/create-profile-image-response.dto';
-import { UserCountDto } from './dto/user-count.dto';
+import { UserResponseDto } from '@app/domain';
+import { UserCountResponseDto } from '@app/domain';
+import { CreateProfileImageDto } from '@app/domain';
+import { CreateProfileImageResponseDto } from '@app/domain';
+import { UserCountDto } from '@app/domain';
 
 @ApiTags('User')
 @Controller('user')

+ 2 - 10
server/apps/immich/src/decorators/auth-user.decorator.ts

@@ -1,14 +1,6 @@
+export { AuthUserDto } from '@app/domain';
+import { AuthUserDto } from '@app/domain';
 import { createParamDecorator, ExecutionContext } from '@nestjs/common';
-// import { AuthUserDto } from './dto/auth-user.dto';
-
-export class AuthUserDto {
-  id!: string;
-  email!: string;
-  isAdmin!: boolean;
-  isPublicUser?: boolean;
-  sharedLinkId?: string;
-  isAllowUpload?: boolean;
-}
 
 export const GetAuthUser = createParamDecorator((data, ctx: ExecutionContext): AuthUserDto => {
   return ctx.switchToHttp().getRequest<{ user: AuthUserDto }>().user;

+ 2 - 2
server/apps/immich/src/global.d.ts

@@ -1,8 +1,8 @@
-import { UserResponseDto } from './api-v1/user/response-dto/user-response.dto';
+import { AuthUserDto } from './decorators/auth-user.decorator';
 
 declare global {
   namespace Express {
     // eslint-disable-next-line @typescript-eslint/no-empty-interface
-    interface User extends UserResponseDto {}
+    interface User extends AuthUserDto {}
   }
 }

+ 1 - 1
server/apps/immich/src/middlewares/admin-role-guard.middleware.ts

@@ -1,6 +1,6 @@
 import { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common';
 import { Request } from 'express';
-import { UserResponseDto } from '../api-v1/user/response-dto/user-response.dto';
+import { UserResponseDto } from '@app/domain';
 
 interface UserRequest extends Request {
   user: UserResponseDto;

+ 1 - 1
server/apps/immich/src/modules/background-task/background-task.module.ts

@@ -1,7 +1,7 @@
 import { BullModule } from '@nestjs/bull';
 import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { AssetEntity, ExifEntity, SmartInfoEntity } from '@app/database';
+import { AssetEntity, ExifEntity, SmartInfoEntity } from '@app/infra';
 import { BackgroundTaskProcessor } from './background-task.processor';
 import { BackgroundTaskService } from './background-task.service';
 

+ 1 - 1
server/apps/immich/src/modules/background-task/background-task.processor.ts

@@ -1,7 +1,7 @@
 import { Process, Processor } from '@nestjs/bull';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Repository } from 'typeorm';
-import { AssetEntity, SmartInfoEntity } from '@app/database';
+import { AssetEntity, SmartInfoEntity } from '@app/infra';
 import { Job } from 'bull';
 import { AssetResponseDto } from '../../api-v1/asset/response-dto/asset-response.dto';
 import { assetUtils } from '@app/common/utils';

+ 1 - 1
server/apps/immich/src/modules/download/download.service.ts

@@ -1,4 +1,4 @@
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { BadRequestException, Injectable, InternalServerErrorException, Logger, StreamableFile } from '@nestjs/common';
 import archiver from 'archiver';
 import { extname } from 'path';

+ 1 - 1
server/apps/immich/src/modules/immich-jwt/immich-jwt.module.ts

@@ -4,7 +4,7 @@ import { JwtModule } from '@nestjs/jwt';
 import { jwtConfig } from '../../config/jwt.config';
 import { JwtStrategy } from './strategies/jwt.strategy';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { APIKeyModule } from '../../api-v1/api-key/api-key.module';
 import { APIKeyStrategy } from './strategies/api-key.strategy';
 import { ShareModule } from '../../api-v1/share/share.module';

+ 1 - 1
server/apps/immich/src/modules/immich-jwt/immich-jwt.service.spec.ts

@@ -1,7 +1,7 @@
 import { Logger } from '@nestjs/common';
 import { JwtService } from '@nestjs/jwt';
 import { Request } from 'express';
-import { UserEntity } from '../../../../../libs/database/src/entities/user.entity';
+import { UserEntity } from '@app/infra';
 import { LoginResponseDto } from '../../api-v1/auth/response-dto/login-response.dto';
 import { AuthType } from '../../constants/jwt.constant';
 import { ImmichJwtService } from './immich-jwt.service';

+ 1 - 1
server/apps/immich/src/modules/immich-jwt/immich-jwt.service.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { Injectable, Logger } from '@nestjs/common';
 import { JwtService } from '@nestjs/jwt';
 import { Request } from 'express';

+ 1 - 1
server/apps/immich/src/modules/immich-jwt/strategies/jwt.strategy.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { Injectable, UnauthorizedException } from '@nestjs/common';
 import { PassportStrategy } from '@nestjs/passport';
 import { InjectRepository } from '@nestjs/typeorm';

+ 1 - 1
server/apps/immich/src/modules/immich-jwt/strategies/public-share.strategy.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { Injectable, UnauthorizedException } from '@nestjs/common';
 import { PassportStrategy } from '@nestjs/passport';
 import { InjectRepository } from '@nestjs/typeorm';

+ 1 - 1
server/apps/immich/src/modules/schedule-tasks/schedule-tasks.module.ts

@@ -1,7 +1,7 @@
 import { BullModule } from '@nestjs/bull';
 import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { AssetEntity, ExifEntity, UserEntity } from '@app/database';
+import { AssetEntity, ExifEntity, UserEntity } from '@app/infra';
 import { ScheduleTasksService } from './schedule-tasks.service';
 import { immichSharedQueues } from '@app/job/constants/bull-queue-registration.constant';
 

+ 1 - 1
server/apps/immich/src/modules/schedule-tasks/schedule-tasks.service.ts

@@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
 import { Cron, CronExpression } from '@nestjs/schedule';
 import { InjectRepository } from '@nestjs/typeorm';
 import { IsNull, Not, Repository } from 'typeorm';
-import { AssetEntity, AssetType, ExifEntity, UserEntity } from '@app/database';
+import { AssetEntity, AssetType, ExifEntity, UserEntity } from '@app/infra';
 import { InjectQueue } from '@nestjs/bull';
 import { Queue } from 'bull';
 import { randomUUID } from 'crypto';

+ 3 - 4
server/apps/immich/test/album.e2e-spec.ts

@@ -3,13 +3,12 @@ import { INestApplication } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import request from 'supertest';
 import { clearDb, getAuthUser, authCustom } from './test-utils';
-import { databaseConfig } from '@app/database';
+import { databaseConfig } from '@app/infra';
 import { AlbumModule } from '../src/api-v1/album/album.module';
 import { CreateAlbumDto } from '../src/api-v1/album/dto/create-album.dto';
 import { ImmichJwtModule } from '../src/modules/immich-jwt/immich-jwt.module';
 import { AuthUserDto } from '../src/decorators/auth-user.decorator';
-import { UserService } from '../src/api-v1/user/user.service';
-import { UserModule } from '../src/api-v1/user/user.module';
+import { UserService } from '@app/domain';
 import { DataSource } from 'typeorm';
 
 function _createAlbum(app: INestApplication, data: CreateAlbumDto) {
@@ -52,7 +51,7 @@ describe('Album', () => {
 
     beforeAll(async () => {
       const builder = Test.createTestingModule({
-        imports: [AlbumModule, UserModule, TypeOrmModule.forRoot(databaseConfig)],
+        imports: [AlbumModule, TypeOrmModule.forRoot(databaseConfig)],
       });
       authUser = getAuthUser(); // set default auth user
       const moduleFixture: TestingModule = await authCustom(builder, () => authUser).compile();

+ 4 - 8
server/apps/immich/test/jest-e2e.json

@@ -7,15 +7,11 @@
     "^.+\\.(t|j)s$": "ts-jest"
   },
   "moduleNameMapper": {
-    "^@app/database(|/.*)$": "<rootDir>../../../libs/database/src/$1",
-    "@app/database/config": "<rootDir>../../../libs/database/src/config",
-    "@app/database/config/(.*)": "<rootDir>../../../libs/database/src/config/$1",
-    "@app/database/entities/(.*)": "<rootDir>../../../libs/database/src/entities/$1",
-    "@app/common": "<rootDir>../../../libs/common/src",
-    "@app/common/(.*)": "<rootDir>../../../libs/common/src/$1",
+    "^@app/common": "<rootDir>../../../libs/common/src",
     "^@app/job(|/.*)$": "<rootDir>../../../libs/job/src/$1",
-    "@app/job": "<rootDir>../../../libs/job/src",
     "^@app/immich-config(|/.*)$": "<rootDir>../../../libs/immich-config/src/$1",
-    "^@app/storage(|/.*)$": "<rootDir>../../../libs/storage/src/$1"
+    "^@app/storage(|/.*)$": "<rootDir>../../../libs/storage/src/$1",
+    "^@app/infra(|/.*)$": "<rootDir>../../../libs/infra/src/$1",
+    "^@app/domain(|/.*)$": "<rootDir>../../../libs/domain/src/$1"
   }
 }

+ 0 - 15
server/apps/immich/test/test-utils.ts

@@ -1,7 +1,6 @@
 import { CanActivate, ExecutionContext } from '@nestjs/common';
 import { TestingModuleBuilder } from '@nestjs/testing';
 import { DataSource } from 'typeorm';
-import { IUserRepository } from '../src/api-v1/user/user-repository';
 import { AuthUserDto } from '../src/decorators/auth-user.decorator';
 import { AuthGuard } from '../src/modules/immich-jwt/guards/auth.guard';
 
@@ -15,20 +14,6 @@ export async function clearDb(db: DataSource) {
   }
 }
 
-export function newUserRepositoryMock(): jest.Mocked<IUserRepository> {
-  return {
-    get: jest.fn(),
-    getAdmin: jest.fn(),
-    getByEmail: jest.fn(),
-    getByOAuthId: jest.fn(),
-    getList: jest.fn(),
-    create: jest.fn(),
-    update: jest.fn(),
-    delete: jest.fn(),
-    restore: jest.fn(),
-  };
-}
-
 export function getAuthUser(): AuthUserDto {
   return {
     id: '3108ac14-8afb-4b7e-87fd-39ebb6b79750',

+ 4 - 7
server/apps/immich/test/user.e2e-spec.ts

@@ -3,12 +3,9 @@ import { INestApplication } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import request from 'supertest';
 import { clearDb, authCustom } from './test-utils';
-import { databaseConfig } from '@app/database/config/database.config';
-import { UserModule } from '../src/api-v1/user/user.module';
+import { databaseConfig } from '@app/infra';
 import { ImmichJwtModule } from '../src/modules/immich-jwt/immich-jwt.module';
-import { UserService } from '../src/api-v1/user/user.service';
-import { CreateAdminDto, CreateUserDto } from '../src/api-v1/user/dto/create-user.dto';
-import { UserResponseDto } from '../src/api-v1/user/response-dto/user-response.dto';
+import { CreateAdminDto, CreateUserDto, UserResponseDto, UserService } from '@app/domain';
 import { DataSource } from 'typeorm';
 
 function _createUser(userService: UserService, data: CreateUserDto | CreateAdminDto) {
@@ -27,7 +24,7 @@ describe('User', () => {
   describe('without auth', () => {
     beforeAll(async () => {
       const moduleFixture: TestingModule = await Test.createTestingModule({
-        imports: [UserModule, ImmichJwtModule, TypeOrmModule.forRoot(databaseConfig)],
+        imports: [ImmichJwtModule, TypeOrmModule.forRoot(databaseConfig)],
       }).compile();
 
       app = moduleFixture.createNestApplication();
@@ -51,7 +48,7 @@ describe('User', () => {
 
     beforeAll(async () => {
       const builder = Test.createTestingModule({
-        imports: [UserModule, TypeOrmModule.forRoot(databaseConfig)],
+        imports: [TypeOrmModule.forRoot(databaseConfig)],
       });
       const moduleFixture: TestingModule = await authCustom(builder, () => authUser).compile();
 

+ 5 - 2
server/apps/microservices/src/microservices.module.ts

@@ -1,5 +1,5 @@
 import { immichAppConfig, immichBullAsyncConfig } from '@app/common/config';
-import { DatabaseModule, AssetEntity, ExifEntity, SmartInfoEntity, UserEntity, APIKeyEntity } from '@app/database';
+import { AssetEntity, ExifEntity, SmartInfoEntity, UserEntity, APIKeyEntity, InfraModule } from '@app/infra';
 import { StorageModule } from '@app/storage';
 import { BullModule } from '@nestjs/bull';
 import { Module } from '@nestjs/common';
@@ -17,11 +17,14 @@ import { ThumbnailGeneratorProcessor } from './processors/thumbnail.processor';
 import { UserDeletionProcessor } from './processors/user-deletion.processor';
 import { VideoTranscodeProcessor } from './processors/video-transcode.processor';
 import { immichSharedQueues } from '@app/job/constants/bull-queue-registration.constant';
+import { DomainModule } from '@app/domain';
 
 @Module({
   imports: [
     ConfigModule.forRoot(immichAppConfig),
-    DatabaseModule,
+    DomainModule.register({
+      imports: [InfraModule],
+    }),
     ImmichConfigModule,
     TypeOrmModule.forFeature([UserEntity, ExifEntity, AssetEntity, SmartInfoEntity, APIKeyEntity]),
     StorageModule,

+ 1 - 1
server/apps/microservices/src/processors/asset-uploaded.processor.ts

@@ -1,4 +1,4 @@
-import { AssetType } from '@app/database';
+import { AssetType } from '@app/infra';
 import {
   IAssetUploadedJob,
   IMetadataExtractionJob,

+ 1 - 1
server/apps/microservices/src/processors/generate-checksum.processor.ts

@@ -1,4 +1,4 @@
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { QueueNameEnum } from '@app/job';
 import { Process, Processor } from '@nestjs/bull';
 import { Logger } from '@nestjs/common';

+ 2 - 2
server/apps/microservices/src/processors/machine-learning.processor.ts

@@ -1,5 +1,5 @@
-import { AssetEntity } from '@app/database';
-import { SmartInfoEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
+import { SmartInfoEntity } from '@app/infra';
 import { MachineLearningJobNameEnum, QueueNameEnum } from '@app/job';
 import { IMachineLearningJob } from '@app/job/interfaces/machine-learning.interface';
 import { Process, Processor } from '@nestjs/bull';

+ 1 - 1
server/apps/microservices/src/processors/metadata-extraction.processor.ts

@@ -1,5 +1,5 @@
 import { ImmichLogLevel } from '@app/common/constants/log-level.constant';
-import { AssetEntity, ExifEntity } from '@app/database';
+import { AssetEntity, ExifEntity } from '@app/infra';
 import {
   IExifExtractionProcessor,
   IVideoLengthExtractionProcessor,

+ 1 - 1
server/apps/microservices/src/processors/storage-migration.processor.ts

@@ -1,5 +1,5 @@
 import { APP_UPLOAD_LOCATION } from '@app/common';
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { ImmichConfigService } from '@app/immich-config';
 import { QueueNameEnum, templateMigrationProcessorName, updateTemplateProcessorName } from '@app/job';
 import { StorageService } from '@app/storage';

+ 1 - 1
server/apps/microservices/src/processors/thumbnail.processor.ts

@@ -1,5 +1,5 @@
 import { APP_UPLOAD_LOCATION } from '@app/common';
-import { AssetEntity, AssetType } from '@app/database';
+import { AssetEntity, AssetType } from '@app/infra';
 import {
   WebpGeneratorProcessor,
   generateJPEGThumbnailProcessorName,

+ 1 - 1
server/apps/microservices/src/processors/user-deletion.processor.ts

@@ -1,5 +1,5 @@
 import { APP_UPLOAD_LOCATION, userUtils } from '@app/common';
-import { APIKeyEntity, AssetEntity, UserEntity } from '@app/database';
+import { APIKeyEntity, AssetEntity, UserEntity } from '@app/infra';
 import { QueueNameEnum, userDeletionProcessorName } from '@app/job';
 import { IUserDeletionJob } from '@app/job/interfaces/user-deletion.interface';
 import { Process, Processor } from '@nestjs/bull';

+ 1 - 1
server/apps/microservices/src/processors/video-transcode.processor.ts

@@ -1,5 +1,5 @@
 import { APP_UPLOAD_LOCATION } from '@app/common/constants';
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { QueueNameEnum } from '@app/job';
 import { mp4ConversionProcessorName } from '@app/job/constants/job-name.constant';
 import { IMp4ConversionProcessor } from '@app/job/interfaces/video-transcode.interface';

+ 471 - 471
server/immich-openapi-specs.json

@@ -473,147 +473,6 @@
         ]
       }
     },
-    "/share": {
-      "get": {
-        "operationId": "getAllSharedLinks",
-        "parameters": [],
-        "responses": {
-          "200": {
-            "description": "",
-            "content": {
-              "application/json": {
-                "schema": {
-                  "type": "array",
-                  "items": {
-                    "$ref": "#/components/schemas/SharedLinkResponseDto"
-                  }
-                }
-              }
-            }
-          }
-        },
-        "tags": [
-          "share"
-        ]
-      }
-    },
-    "/share/me": {
-      "get": {
-        "operationId": "getMySharedLink",
-        "parameters": [],
-        "responses": {
-          "200": {
-            "description": "",
-            "content": {
-              "application/json": {
-                "schema": {
-                  "$ref": "#/components/schemas/SharedLinkResponseDto"
-                }
-              }
-            }
-          }
-        },
-        "tags": [
-          "share"
-        ]
-      }
-    },
-    "/share/{id}": {
-      "get": {
-        "operationId": "getSharedLinkById",
-        "parameters": [
-          {
-            "name": "id",
-            "required": true,
-            "in": "path",
-            "schema": {
-              "type": "string"
-            }
-          }
-        ],
-        "responses": {
-          "200": {
-            "description": "",
-            "content": {
-              "application/json": {
-                "schema": {
-                  "$ref": "#/components/schemas/SharedLinkResponseDto"
-                }
-              }
-            }
-          }
-        },
-        "tags": [
-          "share"
-        ]
-      },
-      "delete": {
-        "operationId": "removeSharedLink",
-        "parameters": [
-          {
-            "name": "id",
-            "required": true,
-            "in": "path",
-            "schema": {
-              "type": "string"
-            }
-          }
-        ],
-        "responses": {
-          "200": {
-            "description": "",
-            "content": {
-              "application/json": {
-                "schema": {
-                  "type": "string"
-                }
-              }
-            }
-          }
-        },
-        "tags": [
-          "share"
-        ]
-      },
-      "patch": {
-        "operationId": "editSharedLink",
-        "parameters": [
-          {
-            "name": "id",
-            "required": true,
-            "in": "path",
-            "schema": {
-              "type": "string"
-            }
-          }
-        ],
-        "requestBody": {
-          "required": true,
-          "content": {
-            "application/json": {
-              "schema": {
-                "$ref": "#/components/schemas/EditSharedLinkDto"
-              }
-            }
-          }
-        },
-        "responses": {
-          "200": {
-            "description": "",
-            "content": {
-              "application/json": {
-                "schema": {
-                  "$ref": "#/components/schemas/SharedLinkResponseDto"
-                }
-              }
-            }
-          }
-        },
-        "tags": [
-          "share"
-        ]
-      }
-    },
     "/asset/upload": {
       "post": {
         "operationId": "uploadFile",
@@ -1370,9 +1229,9 @@
         ]
       }
     },
-    "/album/count-by-user-id": {
+    "/share": {
       "get": {
-        "operationId": "getAlbumCountByUserId",
+        "operationId": "getAllSharedLinks",
         "parameters": [],
         "responses": {
           "200": {
@@ -1380,73 +1239,49 @@
             "content": {
               "application/json": {
                 "schema": {
-                  "$ref": "#/components/schemas/AlbumCountResponseDto"
+                  "type": "array",
+                  "items": {
+                    "$ref": "#/components/schemas/SharedLinkResponseDto"
+                  }
                 }
               }
             }
           }
         },
         "tags": [
-          "Album"
-        ],
-        "security": [
-          {
-            "bearer": []
-          }
+          "share"
         ]
       }
     },
-    "/album": {
-      "post": {
-        "operationId": "createAlbum",
+    "/share/me": {
+      "get": {
+        "operationId": "getMySharedLink",
         "parameters": [],
-        "requestBody": {
-          "required": true,
-          "content": {
-            "application/json": {
-              "schema": {
-                "$ref": "#/components/schemas/CreateAlbumDto"
-              }
-            }
-          }
-        },
         "responses": {
-          "201": {
+          "200": {
             "description": "",
             "content": {
               "application/json": {
                 "schema": {
-                  "$ref": "#/components/schemas/AlbumResponseDto"
+                  "$ref": "#/components/schemas/SharedLinkResponseDto"
                 }
               }
             }
           }
         },
         "tags": [
-          "Album"
-        ],
-        "security": [
-          {
-            "bearer": []
-          }
+          "share"
         ]
-      },
+      }
+    },
+    "/share/{id}": {
       "get": {
-        "operationId": "getAllAlbums",
+        "operationId": "getSharedLinkById",
         "parameters": [
           {
-            "name": "shared",
-            "required": false,
-            "in": "query",
-            "schema": {
-              "type": "boolean"
-            }
-          },
-          {
-            "name": "assetId",
-            "required": false,
-            "in": "query",
-            "description": "Only returns albums that contain the asset\nIgnores the shared parameter\nundefined: get all albums",
+            "name": "id",
+            "required": true,
+            "in": "path",
             "schema": {
               "type": "string"
             }
@@ -1458,31 +1293,21 @@
             "content": {
               "application/json": {
                 "schema": {
-                  "type": "array",
-                  "items": {
-                    "$ref": "#/components/schemas/AlbumResponseDto"
-                  }
+                  "$ref": "#/components/schemas/SharedLinkResponseDto"
                 }
               }
             }
           }
         },
         "tags": [
-          "Album"
-        ],
-        "security": [
-          {
-            "bearer": []
-          }
+          "share"
         ]
-      }
-    },
-    "/album/{albumId}/users": {
-      "put": {
-        "operationId": "addUsersToAlbum",
+      },
+      "delete": {
+        "operationId": "removeSharedLink",
         "parameters": [
           {
-            "name": "albumId",
+            "name": "id",
             "required": true,
             "in": "path",
             "schema": {
@@ -1490,44 +1315,219 @@
             }
           }
         ],
-        "requestBody": {
-          "required": true,
-          "content": {
-            "application/json": {
-              "schema": {
-                "$ref": "#/components/schemas/AddUsersDto"
-              }
-            }
-          }
-        },
         "responses": {
           "200": {
             "description": "",
             "content": {
               "application/json": {
                 "schema": {
-                  "$ref": "#/components/schemas/AlbumResponseDto"
+                  "type": "string"
                 }
               }
             }
           }
         },
         "tags": [
-          "Album"
-        ],
-        "security": [
-          {
-            "bearer": []
-          }
+          "share"
         ]
-      }
-    },
-    "/album/{albumId}/assets": {
-      "put": {
-        "operationId": "addAssetsToAlbum",
+      },
+      "patch": {
+        "operationId": "editSharedLink",
         "parameters": [
           {
-            "name": "albumId",
+            "name": "id",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/EditSharedLinkDto"
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/SharedLinkResponseDto"
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "share"
+        ]
+      }
+    },
+    "/album/count-by-user-id": {
+      "get": {
+        "operationId": "getAlbumCountByUserId",
+        "parameters": [],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/AlbumCountResponseDto"
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "Album"
+        ],
+        "security": [
+          {
+            "bearer": []
+          }
+        ]
+      }
+    },
+    "/album": {
+      "post": {
+        "operationId": "createAlbum",
+        "parameters": [],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/CreateAlbumDto"
+              }
+            }
+          }
+        },
+        "responses": {
+          "201": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/AlbumResponseDto"
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "Album"
+        ],
+        "security": [
+          {
+            "bearer": []
+          }
+        ]
+      },
+      "get": {
+        "operationId": "getAllAlbums",
+        "parameters": [
+          {
+            "name": "shared",
+            "required": false,
+            "in": "query",
+            "schema": {
+              "type": "boolean"
+            }
+          },
+          {
+            "name": "assetId",
+            "required": false,
+            "in": "query",
+            "description": "Only returns albums that contain the asset\nIgnores the shared parameter\nundefined: get all albums",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "$ref": "#/components/schemas/AlbumResponseDto"
+                  }
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "Album"
+        ],
+        "security": [
+          {
+            "bearer": []
+          }
+        ]
+      }
+    },
+    "/album/{albumId}/users": {
+      "put": {
+        "operationId": "addUsersToAlbum",
+        "parameters": [
+          {
+            "name": "albumId",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/AddUsersDto"
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/AlbumResponseDto"
+                }
+              }
+            }
+          }
+        },
+        "tags": [
+          "Album"
+        ],
+        "security": [
+          {
+            "bearer": []
+          }
+        ]
+      }
+    },
+    "/album/{albumId}/assets": {
+      "put": {
+        "operationId": "addAssetsToAlbum",
+        "parameters": [
+          {
+            "name": "albumId",
             "required": true,
             "in": "path",
             "schema": {
@@ -2879,38 +2879,141 @@
           "name"
         ]
       },
-      "SharedLinkType": {
-        "type": "string",
-        "enum": [
-          "ALBUM",
-          "INDIVIDUAL"
+      "AssetFileUploadDto": {
+        "type": "object",
+        "properties": {
+          "assetData": {
+            "type": "string",
+            "format": "binary"
+          }
+        },
+        "required": [
+          "assetData"
         ]
       },
-      "AssetTypeEnum": {
+      "AssetFileUploadResponseDto": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string"
+          }
+        },
+        "required": [
+          "id"
+        ]
+      },
+      "DownloadFilesDto": {
+        "type": "object",
+        "properties": {
+          "assetIds": {
+            "title": "Array of asset ids to be downloaded",
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          }
+        },
+        "required": [
+          "assetIds"
+        ]
+      },
+      "ThumbnailFormat": {
         "type": "string",
         "enum": [
-          "IMAGE",
-          "VIDEO",
-          "AUDIO",
-          "OTHER"
+          "JPEG",
+          "WEBP"
         ]
       },
-      "ExifResponseDto": {
+      "CuratedObjectsResponseDto": {
         "type": "object",
         "properties": {
           "id": {
-            "type": "integer",
-            "nullable": true,
-            "default": null,
-            "format": "int64"
+            "type": "string"
           },
-          "fileSizeInByte": {
-            "type": "integer",
-            "nullable": true,
-            "default": null,
-            "format": "int64"
+          "object": {
+            "type": "string"
           },
-          "make": {
+          "resizePath": {
+            "type": "string"
+          },
+          "deviceAssetId": {
+            "type": "string"
+          },
+          "deviceId": {
+            "type": "string"
+          }
+        },
+        "required": [
+          "id",
+          "object",
+          "resizePath",
+          "deviceAssetId",
+          "deviceId"
+        ]
+      },
+      "CuratedLocationsResponseDto": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string"
+          },
+          "city": {
+            "type": "string"
+          },
+          "resizePath": {
+            "type": "string"
+          },
+          "deviceAssetId": {
+            "type": "string"
+          },
+          "deviceId": {
+            "type": "string"
+          }
+        },
+        "required": [
+          "id",
+          "city",
+          "resizePath",
+          "deviceAssetId",
+          "deviceId"
+        ]
+      },
+      "SearchAssetDto": {
+        "type": "object",
+        "properties": {
+          "searchTerm": {
+            "type": "string"
+          }
+        },
+        "required": [
+          "searchTerm"
+        ]
+      },
+      "AssetTypeEnum": {
+        "type": "string",
+        "enum": [
+          "IMAGE",
+          "VIDEO",
+          "AUDIO",
+          "OTHER"
+        ]
+      },
+      "ExifResponseDto": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "integer",
+            "nullable": true,
+            "default": null,
+            "format": "int64"
+          },
+          "fileSizeInByte": {
+            "type": "integer",
+            "nullable": true,
+            "default": null,
+            "format": "int64"
+          },
+          "make": {
             "type": "string",
             "nullable": true,
             "default": null
@@ -3144,232 +3247,6 @@
           "tags"
         ]
       },
-      "AlbumResponseDto": {
-        "type": "object",
-        "properties": {
-          "assetCount": {
-            "type": "integer"
-          },
-          "id": {
-            "type": "string"
-          },
-          "ownerId": {
-            "type": "string"
-          },
-          "albumName": {
-            "type": "string"
-          },
-          "createdAt": {
-            "type": "string"
-          },
-          "albumThumbnailAssetId": {
-            "type": "string",
-            "nullable": true
-          },
-          "shared": {
-            "type": "boolean"
-          },
-          "sharedUsers": {
-            "type": "array",
-            "items": {
-              "$ref": "#/components/schemas/UserResponseDto"
-            }
-          },
-          "assets": {
-            "type": "array",
-            "items": {
-              "$ref": "#/components/schemas/AssetResponseDto"
-            }
-          }
-        },
-        "required": [
-          "assetCount",
-          "id",
-          "ownerId",
-          "albumName",
-          "createdAt",
-          "albumThumbnailAssetId",
-          "shared",
-          "sharedUsers",
-          "assets"
-        ]
-      },
-      "SharedLinkResponseDto": {
-        "type": "object",
-        "properties": {
-          "type": {
-            "$ref": "#/components/schemas/SharedLinkType"
-          },
-          "id": {
-            "type": "string"
-          },
-          "description": {
-            "type": "string"
-          },
-          "userId": {
-            "type": "string"
-          },
-          "key": {
-            "type": "string"
-          },
-          "createdAt": {
-            "type": "string"
-          },
-          "expiresAt": {
-            "type": "string",
-            "nullable": true
-          },
-          "assets": {
-            "type": "array",
-            "items": {
-              "$ref": "#/components/schemas/AssetResponseDto"
-            }
-          },
-          "album": {
-            "$ref": "#/components/schemas/AlbumResponseDto"
-          },
-          "allowUpload": {
-            "type": "boolean"
-          }
-        },
-        "required": [
-          "type",
-          "id",
-          "userId",
-          "key",
-          "createdAt",
-          "expiresAt",
-          "assets",
-          "allowUpload"
-        ]
-      },
-      "EditSharedLinkDto": {
-        "type": "object",
-        "properties": {
-          "description": {
-            "type": "string"
-          },
-          "expiredAt": {
-            "type": "string"
-          },
-          "allowUpload": {
-            "type": "boolean"
-          },
-          "isEditExpireTime": {
-            "type": "boolean"
-          }
-        }
-      },
-      "AssetFileUploadDto": {
-        "type": "object",
-        "properties": {
-          "assetData": {
-            "type": "string",
-            "format": "binary"
-          }
-        },
-        "required": [
-          "assetData"
-        ]
-      },
-      "AssetFileUploadResponseDto": {
-        "type": "object",
-        "properties": {
-          "id": {
-            "type": "string"
-          }
-        },
-        "required": [
-          "id"
-        ]
-      },
-      "DownloadFilesDto": {
-        "type": "object",
-        "properties": {
-          "assetIds": {
-            "title": "Array of asset ids to be downloaded",
-            "type": "array",
-            "items": {
-              "type": "string"
-            }
-          }
-        },
-        "required": [
-          "assetIds"
-        ]
-      },
-      "ThumbnailFormat": {
-        "type": "string",
-        "enum": [
-          "JPEG",
-          "WEBP"
-        ]
-      },
-      "CuratedObjectsResponseDto": {
-        "type": "object",
-        "properties": {
-          "id": {
-            "type": "string"
-          },
-          "object": {
-            "type": "string"
-          },
-          "resizePath": {
-            "type": "string"
-          },
-          "deviceAssetId": {
-            "type": "string"
-          },
-          "deviceId": {
-            "type": "string"
-          }
-        },
-        "required": [
-          "id",
-          "object",
-          "resizePath",
-          "deviceAssetId",
-          "deviceId"
-        ]
-      },
-      "CuratedLocationsResponseDto": {
-        "type": "object",
-        "properties": {
-          "id": {
-            "type": "string"
-          },
-          "city": {
-            "type": "string"
-          },
-          "resizePath": {
-            "type": "string"
-          },
-          "deviceAssetId": {
-            "type": "string"
-          },
-          "deviceId": {
-            "type": "string"
-          }
-        },
-        "required": [
-          "id",
-          "city",
-          "resizePath",
-          "deviceAssetId",
-          "deviceId"
-        ]
-      },
-      "SearchAssetDto": {
-        "type": "object",
-        "properties": {
-          "searchTerm": {
-            "type": "string"
-          }
-        },
-        "required": [
-          "searchTerm"
-        ]
-      },
       "TimeGroupEnum": {
         "type": "string",
         "enum": [
@@ -3596,6 +3473,129 @@
           "existingIds"
         ]
       },
+      "SharedLinkType": {
+        "type": "string",
+        "enum": [
+          "ALBUM",
+          "INDIVIDUAL"
+        ]
+      },
+      "AlbumResponseDto": {
+        "type": "object",
+        "properties": {
+          "assetCount": {
+            "type": "integer"
+          },
+          "id": {
+            "type": "string"
+          },
+          "ownerId": {
+            "type": "string"
+          },
+          "albumName": {
+            "type": "string"
+          },
+          "createdAt": {
+            "type": "string"
+          },
+          "albumThumbnailAssetId": {
+            "type": "string",
+            "nullable": true
+          },
+          "shared": {
+            "type": "boolean"
+          },
+          "sharedUsers": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/UserResponseDto"
+            }
+          },
+          "assets": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/AssetResponseDto"
+            }
+          }
+        },
+        "required": [
+          "assetCount",
+          "id",
+          "ownerId",
+          "albumName",
+          "createdAt",
+          "albumThumbnailAssetId",
+          "shared",
+          "sharedUsers",
+          "assets"
+        ]
+      },
+      "SharedLinkResponseDto": {
+        "type": "object",
+        "properties": {
+          "type": {
+            "$ref": "#/components/schemas/SharedLinkType"
+          },
+          "id": {
+            "type": "string"
+          },
+          "description": {
+            "type": "string"
+          },
+          "userId": {
+            "type": "string"
+          },
+          "key": {
+            "type": "string"
+          },
+          "createdAt": {
+            "type": "string"
+          },
+          "expiresAt": {
+            "type": "string",
+            "nullable": true
+          },
+          "assets": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/AssetResponseDto"
+            }
+          },
+          "album": {
+            "$ref": "#/components/schemas/AlbumResponseDto"
+          },
+          "allowUpload": {
+            "type": "boolean"
+          }
+        },
+        "required": [
+          "type",
+          "id",
+          "userId",
+          "key",
+          "createdAt",
+          "expiresAt",
+          "assets",
+          "allowUpload"
+        ]
+      },
+      "EditSharedLinkDto": {
+        "type": "object",
+        "properties": {
+          "description": {
+            "type": "string"
+          },
+          "expiredAt": {
+            "type": "string"
+          },
+          "allowUpload": {
+            "type": "boolean"
+          },
+          "isEditExpireTime": {
+            "type": "boolean"
+          }
+        }
+      },
       "AlbumCountResponseDto": {
         "type": "object",
         "properties": {

+ 1 - 1
server/libs/common/src/utils/asset-utils.ts

@@ -1,4 +1,4 @@
-import { AssetEntity } from '@app/database';
+import { AssetEntity } from '@app/infra';
 import { AssetResponseDto } from 'apps/immich/src/api-v1/asset/response-dto/asset-response.dto';
 import fs from 'fs';
 

+ 1 - 1
server/libs/common/src/utils/user-utils.spec.ts

@@ -1,6 +1,6 @@
 // create unit test for user utils
 
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 import { userUtils } from './user-utils';
 
 describe('User Utilities', () => {

+ 1 - 1
server/libs/common/src/utils/user-utils.ts

@@ -1,4 +1,4 @@
-import { UserEntity } from '@app/database';
+import { UserEntity } from '@app/infra';
 
 function createUserUtils() {
   const isReadyForDeletion = (user: UserEntity): boolean => {

+ 0 - 10
server/libs/database/src/database.module.ts

@@ -1,10 +0,0 @@
-import { Module } from '@nestjs/common';
-import { TypeOrmModule } from '@nestjs/typeorm';
-import { databaseConfig } from './config/database.config';
-
-@Module({
-  imports: [TypeOrmModule.forRoot(databaseConfig)],
-  providers: [],
-  exports: [TypeOrmModule],
-})
-export class DatabaseModule {}

+ 8 - 0
server/libs/domain/src/auth/dto/auth-user.dto.ts

@@ -0,0 +1,8 @@
+export class AuthUserDto {
+  id!: string;
+  email!: string;
+  isAdmin!: boolean;
+  isPublicUser?: boolean;
+  sharedLinkId?: string;
+  isAllowUpload?: boolean;
+}

+ 1 - 0
server/libs/domain/src/auth/dto/index.ts

@@ -0,0 +1 @@
+export * from './auth-user.dto';

+ 1 - 0
server/libs/domain/src/auth/index.ts

@@ -0,0 +1 @@
+export * from './dto';

+ 20 - 0
server/libs/domain/src/domain.module.ts

@@ -0,0 +1,20 @@
+import { DynamicModule, Global, Module, ModuleMetadata, Provider } from '@nestjs/common';
+import { UserService } from './user';
+
+const providers: Provider[] = [
+  //
+  UserService,
+];
+
+@Global()
+@Module({})
+export class DomainModule {
+  static register(options: Pick<ModuleMetadata, 'imports'>): DynamicModule {
+    return {
+      module: DomainModule,
+      imports: options.imports,
+      providers: [...providers],
+      exports: [...providers],
+    };
+  }
+}

+ 3 - 0
server/libs/domain/src/index.ts

@@ -0,0 +1,3 @@
+export * from './auth';
+export * from './domain.module';
+export * from './user';

+ 0 - 0
server/apps/immich/src/api-v1/user/dto/create-profile-image.dto.ts → server/libs/domain/src/user/dto/create-profile-image.dto.ts


+ 0 - 0
server/apps/immich/src/api-v1/user/dto/create-user.dto.spec.ts → server/libs/domain/src/user/dto/create-user.dto.spec.ts


+ 0 - 0
server/apps/immich/src/api-v1/user/dto/create-user.dto.ts → server/libs/domain/src/user/dto/create-user.dto.ts


+ 4 - 0
server/libs/domain/src/user/dto/index.ts

@@ -0,0 +1,4 @@
+export * from './create-profile-image.dto';
+export * from './create-user.dto';
+export * from './update-user.dto';
+export * from './user-count.dto';

Some files were not shown because too many files changed in this diff