1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-22 01:47:08 +02:00

chore(server): use absolute import paths (#8080)

update server to use absolute import paths
This commit is contained in:
Daniel Dietzler 2024-03-20 19:32:04 +01:00 committed by GitHub
parent 591a641d8d
commit 30f499cf2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
347 changed files with 1962 additions and 2274 deletions

30
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"editor.formatOnSave": true,
"[javascript][typescript][css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.formatOnSave": true
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode",
"editor.tabSize": 2
},
"svelte.enable-ts-plugin": true,
"eslint.validate": [
"javascript",
"svelte"
],
"typescript.preferences.importModuleSpecifier": "non-relative",
"[dart]": {
"editor.formatOnSave": true,
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off",
"editor.defaultFormatter": "Dart-Code.dart-code"
},
"cSpell.words": [
"immich"
],
}

View File

@ -33,5 +33,6 @@ module.exports = {
'@typescript-eslint/require-await': 'error',
curly: 2,
'prettier/prettier': 0,
'no-restricted-imports': ['error', { patterns: [{ group: ['.*'], message: 'Relative imports are not allowed.' }] }],
},
};

View File

@ -1,4 +1,4 @@
import { AssetResponseDto } from '@app/domain';
import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto';
import request from 'supertest';
export const assetApi = {

View File

@ -1,6 +1,7 @@
import { LoginResponseDto, UserResponseDto } from '@app/domain';
import { adminSignupStub, loginResponseStub, loginStub } from '@test';
import { LoginResponseDto } from 'src/domain/auth/auth.dto';
import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto';
import request from 'supertest';
import { adminSignupStub, loginResponseStub, loginStub } from 'test/fixtures/auth.stub';
export const authApi = {
adminSignUp: async (server: any) => {

View File

@ -1,6 +1,6 @@
import { assetApi } from './asset-api';
import { authApi } from './auth-api';
import { libraryApi } from './library-api';
import { assetApi } from 'e2e/client/asset-api';
import { authApi } from 'e2e/client/auth-api';
import { libraryApi } from 'e2e/client/library-api';
export const api = {
authApi,

View File

@ -1,4 +1,4 @@
import { CreateLibraryDto, LibraryResponseDto, ScanLibraryDto } from '@app/domain';
import { CreateLibraryDto, LibraryResponseDto, ScanLibraryDto } from 'src/domain/library/library.dto';
import request from 'supertest';
export const libraryApi = {

View File

@ -16,9 +16,7 @@
],
"coverageDirectory": "./coverage",
"moduleNameMapper": {
"^@test(|/.*)$": "<rootDir>/test/$1",
"^@app/immich(|/.*)$": "<rootDir>/src/immich/$1",
"^@app/infra(|/.*)$": "<rootDir>/src/infra/$1",
"^@app/domain(|/.*)$": "<rootDir>/src/domain/$1"
"^test(|/.*)$": "<rootDir>/test/$1",
"^src(|/.*)$": "<rootDir>/src/$1"
}
}

View File

@ -1,15 +1,19 @@
import { LibraryResponseDto, LibraryService, LoginResponseDto, StorageEventType } from '@app/domain';
import { AssetType, LibraryType } from '@app/infra/entities';
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 { StorageEventType } from 'src/domain/repositories/storage.repository';
import { AssetType } from 'src/infra/entities/asset.entity';
import { LibraryType } from 'src/infra/entities/library.entity';
import {
IMMICH_TEST_ASSET_PATH,
IMMICH_TEST_ASSET_TEMP_PATH,
restoreTempFolder,
testApp,
waitForEvent,
} from '../../../src/test-utils/utils';
import { api } from '../../client';
} from 'src/test-utils/utils';
describe(`Library watcher (e2e)`, () => {
let server: any;

View File

@ -1,17 +1,13 @@
import { LoginResponseDto } from '@app/domain';
import { LibraryController } from '@app/immich';
import { LibraryType } from '@app/infra/entities';
import { errorStub, uuidStub } from '@test/fixtures';
import * as fs from 'node:fs';
import { api } from 'e2e/client';
import fs from 'node:fs';
import { LoginResponseDto } from 'src/domain/auth/auth.dto';
import { LibraryController } from 'src/immich/controllers/library.controller';
import { LibraryType } from 'src/infra/entities/library.entity';
import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, restoreTempFolder, testApp } from 'src/test-utils/utils';
import request from 'supertest';
import { errorStub } from 'test/fixtures/error.stub';
import { uuidStub } from 'test/fixtures/uuid.stub';
import { utimes } from 'utimes';
import {
IMMICH_TEST_ASSET_PATH,
IMMICH_TEST_ASSET_TEMP_PATH,
restoreTempFolder,
testApp,
} from '../../../src/test-utils/utils';
import { api } from '../../client';
describe(`${LibraryController.name} (e2e)`, () => {
let server: any;

View File

@ -155,16 +155,14 @@
"./src/domain/": {
"branches": 75,
"functions": 80,
"lines": 90,
"statements": 90
"lines": 85,
"statements": 85
}
},
"testEnvironment": "node",
"moduleNameMapper": {
"^@test(|/.*)$": "<rootDir>/test/$1",
"^@app/immich(|/.*)$": "<rootDir>/src/immich/$1",
"^@app/infra(|/.*)$": "<rootDir>/src/infra/$1",
"^@app/domain(|/.*)$": "<rootDir>/src/domain/$1"
"^test(|/.*)$": "<rootDir>/test/$1",
"^src(|/.*)$": "<rootDir>/src/$1"
},
"globalSetup": "<rootDir>/test/global-setup.js"
},

View File

@ -1,8 +1,8 @@
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { SharedLinkEntity } from '../../infra/entities';
import { AuthDto } from '../auth';
import { setDifference, setIsEqual, setUnion } from '../domain.util';
import { IAccessRepository } from '../repositories';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { setDifference, setIsEqual, setUnion } from 'src/domain/domain.util';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { SharedLinkEntity } from 'src/infra/entities/shared-link.entity';
export enum Permission {
ACTIVITY_CREATE = 'activity.create',

View File

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

View File

@ -1,8 +1,8 @@
import { ActivityEntity } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
import { Optional, ValidateUUID } from '../domain.util';
import { UserDto, mapSimpleUser } from '../user/response-dto';
import { Optional, ValidateUUID } from 'src/domain/domain.util';
import { UserDto, mapSimpleUser } from 'src/domain/user/response-dto/user-response.dto';
import { ActivityEntity } from 'src/infra/entities/activity.entity';
export enum ReactionType {
COMMENT = 'comment',

View File

@ -1,8 +1,5 @@
import { ActivityEntity } from '@app/infra/entities';
import { Inject, Injectable } from '@nestjs/common';
import { AccessCore, Permission } from '../access';
import { AuthDto } from '../auth';
import { IAccessRepository, IActivityRepository } from '../repositories';
import { AccessCore, Permission } from 'src/domain/access/access.core';
import {
ActivityCreateDto,
ActivityDto,
@ -13,7 +10,11 @@ import {
ReactionLevel,
ReactionType,
mapActivity,
} from './activity.dto';
} from 'src/domain/activity/activity.dto';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IActivityRepository } from 'src/domain/repositories/activity.repository';
import { ActivityEntity } from 'src/infra/entities/activity.entity';
@Injectable()
export class ActivityService {

View File

@ -1,10 +1,11 @@
import { BadRequestException } from '@nestjs/common';
import { authStub, IAccessRepositoryMock, newAccessRepositoryMock } from '@test';
import { activityStub } from '@test/fixtures/activity.stub';
import { newActivityRepositoryMock } from '@test/repositories/activity.repository.mock';
import { IActivityRepository } from '../repositories';
import { ReactionType } from './activity.dto';
import { ActivityService } from './activity.service';
import { ReactionType } from 'src/domain/activity/activity.dto';
import { ActivityService } from 'src/domain/activity/activity.service';
import { IActivityRepository } from 'src/domain/repositories/activity.repository';
import { activityStub } from 'test/fixtures/activity.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newActivityRepositoryMock } from 'test/repositories/activity.repository.mock';
describe(ActivityService.name, () => {
let sut: ActivityService;

View File

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

View File

@ -1,5 +1,5 @@
import { albumStub } from '@test';
import { mapAlbum } from './album-response.dto';
import { mapAlbum } from 'src/domain/album/album-response.dto';
import { albumStub } from 'test/fixtures/album.stub';
describe('mapAlbum', () => {
it('should set start and end dates', () => {

View File

@ -1,9 +1,9 @@
import { AlbumEntity, AssetOrder } from '@app/infra/entities';
import { Optional } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import { AssetResponseDto, mapAsset } from '../asset';
import { AuthDto } from '../auth/auth.dto';
import { UserResponseDto, mapUser } from '../user';
import { AssetResponseDto, mapAsset } from 'src/domain/asset/response-dto/asset-response.dto';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { Optional } from 'src/domain/domain.util';
import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto';
import { AlbumEntity, AssetOrder } from 'src/infra/entities/album.entity';
export class AlbumResponseDto {
id!: string;

View File

@ -1,36 +1,32 @@
import { BadRequestException } from '@nestjs/common';
import {
albumStub,
authStub,
IAccessRepositoryMock,
newAccessRepositoryMock,
newAlbumRepositoryMock,
newAssetRepositoryMock,
newJobRepositoryMock,
newUserRepositoryMock,
userStub,
} from '@test';
import _ from 'lodash';
import { BulkIdErrorReason } from '../asset';
import { IAlbumRepository, IAssetRepository, IJobRepository, IUserRepository } from '../repositories';
import { AlbumService } from './album.service';
import { AlbumService } from 'src/domain/album/album.service';
import { BulkIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto';
import { IAlbumRepository } from 'src/domain/repositories/album.repository';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { albumStub } from 'test/fixtures/album.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { userStub } from 'test/fixtures/user.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
describe(AlbumService.name, () => {
let sut: AlbumService;
let accessMock: IAccessRepositoryMock;
let albumMock: jest.Mocked<IAlbumRepository>;
let assetMock: jest.Mocked<IAssetRepository>;
let jobMock: jest.Mocked<IJobRepository>;
let userMock: jest.Mocked<IUserRepository>;
beforeEach(() => {
accessMock = newAccessRepositoryMock();
albumMock = newAlbumRepositoryMock();
assetMock = newAssetRepositoryMock();
jobMock = newJobRepositoryMock();
userMock = newUserRepositoryMock();
sut = new AlbumService(accessMock, albumMock, assetMock, jobMock, userMock);
sut = new AlbumService(accessMock, albumMock, assetMock, userMock);
});
it('should work', () => {

View File

@ -1,26 +1,27 @@
import { AlbumEntity, AssetEntity, UserEntity } from '@app/infra/entities';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { AccessCore, Permission } from '../access';
import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from '../asset';
import { AuthDto } from '../auth';
import { setUnion } from '../domain.util';
import {
AlbumAssetCount,
AlbumInfoOptions,
IAccessRepository,
IAlbumRepository,
IAssetRepository,
IJobRepository,
IUserRepository,
} from '../repositories';
import { AccessCore, Permission } from 'src/domain/access/access.core';
import {
AlbumCountResponseDto,
AlbumResponseDto,
mapAlbum,
mapAlbumWithAssets,
mapAlbumWithoutAssets,
} from './album-response.dto';
import { AddUsersDto, AlbumInfoDto, CreateAlbumDto, GetAlbumsDto, UpdateAlbumDto } from './dto';
} 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';
import { setUnion } from 'src/domain/domain.util';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/domain/repositories/album.repository';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { AlbumEntity } from 'src/infra/entities/album.entity';
import { AssetEntity } from 'src/infra/entities/asset.entity';
import { UserEntity } from 'src/infra/entities/user.entity';
@Injectable()
export class AlbumService {
@ -29,7 +30,6 @@ export class AlbumService {
@Inject(IAccessRepository) accessRepository: IAccessRepository,
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(IUserRepository) private userRepository: IUserRepository,
) {
this.access = AccessCore.create(accessRepository);

View File

@ -1,5 +1,5 @@
import { ArrayNotEmpty } from 'class-validator';
import { ValidateUUID } from '../../domain.util';
import { ValidateUUID } from 'src/domain/domain.util';
export class AddUsersDto {
@ValidateUUID({ each: true })

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
import { Optional, ValidateUUID } from '../../domain.util';
import { Optional, ValidateUUID } from 'src/domain/domain.util';
export class CreateAlbumDto {
@IsString()

View File

@ -1,7 +1,7 @@
import { AssetOrder } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsString } from 'class-validator';
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
import { Optional, ValidateBoolean, ValidateUUID } from 'src/domain/domain.util';
import { AssetOrder } from 'src/infra/entities/album.entity';
export class UpdateAlbumDto {
@Optional()

View File

@ -1,4 +1,4 @@
import { ValidateBoolean } from '../../domain.util';
import { ValidateBoolean } from 'src/domain/domain.util';
export class AlbumInfoDto {
@ValidateBoolean({ optional: true })

View File

@ -1,4 +1,4 @@
import { ValidateBoolean, ValidateUUID } from '../../domain.util';
import { ValidateBoolean, ValidateUUID } from 'src/domain/domain.util';
export class GetAlbumsDto {
@ValidateBoolean({ optional: true })

View File

@ -1,5 +0,0 @@
export * from './album-add-users.dto';
export * from './album-create.dto';
export * from './album-update.dto';
export * from './album.dto';
export * from './get-albums.dto';

View File

@ -1,3 +0,0 @@
export * from './album-response.dto';
export * from './album.service';
export * from './dto';

View File

@ -1,5 +1,5 @@
import { IsNotEmpty, IsString } from 'class-validator';
import { Optional } from '../domain.util';
import { Optional } from 'src/domain/domain.util';
export class APIKeyCreateDto {
@IsString()
@IsNotEmpty()

View File

@ -1,7 +1,11 @@
import { BadRequestException } from '@nestjs/common';
import { authStub, keyStub, newCryptoRepositoryMock, newKeyRepositoryMock } from '@test';
import { ICryptoRepository, IKeyRepository } from '../repositories';
import { APIKeyService } from './api-key.service';
import { APIKeyService } from 'src/domain/api-key/api-key.service';
import { IKeyRepository } from 'src/domain/repositories/api-key.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { keyStub } from 'test/fixtures/api-key.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
describe(APIKeyService.name, () => {
let sut: APIKeyService;

View File

@ -1,8 +1,9 @@
import { APIKeyEntity } from '@app/infra/entities';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { AuthDto } from '../auth';
import { ICryptoRepository, IKeyRepository } from '../repositories';
import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto } from './api-key.dto';
import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto } from 'src/domain/api-key/api-key.dto';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { IKeyRepository } from 'src/domain/repositories/api-key.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { APIKeyEntity } from 'src/infra/entities/api-key.entity';
@Injectable()
export class APIKeyService {

View File

@ -1,2 +0,0 @@
export * from './api-key.dto';
export * from './api-key.service';

View File

@ -1,42 +1,33 @@
import { AssetEntity, AssetType } from '@app/infra/entities';
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import {
IAccessRepositoryMock,
assetStackStub,
assetStub,
authStub,
faceStub,
newAccessRepositoryMock,
newAssetRepositoryMock,
newAssetStackRepositoryMock,
newCommunicationRepositoryMock,
newJobRepositoryMock,
newPartnerRepositoryMock,
newStorageRepositoryMock,
newSystemConfigRepositoryMock,
newUserRepositoryMock,
partnerStub,
userStub,
} from '@test';
import { when } from 'jest-when';
import { JobName } from '../job';
import {
AssetStats,
ClientEvent,
IAssetRepository,
IAssetStackRepository,
ICommunicationRepository,
IJobRepository,
IPartnerRepository,
IStorageRepository,
ISystemConfigRepository,
IUserRepository,
JobItem,
TimeBucketSize,
} from '../repositories';
import { AssetService, UploadFieldName } from './asset.service';
import { AssetJobName, AssetStatsResponseDto } from './dto';
import { mapAsset } from './response-dto';
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 { JobName } from 'src/domain/job/job.constants';
import { IAssetStackRepository } from 'src/domain/repositories/asset-stack.repository';
import { AssetStats, IAssetRepository, TimeBucketSize } from 'src/domain/repositories/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { IJobRepository, JobItem } from 'src/domain/repositories/job.repository';
import { IPartnerRepository } from 'src/domain/repositories/partner.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { AssetEntity, AssetType } from 'src/infra/entities/asset.entity';
import { assetStackStub, assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { faceStub } from 'test/fixtures/face.stub';
import { partnerStub } from 'test/fixtures/partner.stub';
import { userStub } from 'test/fixtures/user.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAssetStackRepositoryMock } from 'test/repositories/asset-stack.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
const stats: AssetStats = {
[AssetType.IMAGE]: 10,

View File

@ -1,54 +1,43 @@
import { AssetEntity, LibraryType } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, Inject } from '@nestjs/common';
import _ from 'lodash';
import { DateTime, Duration } from 'luxon';
import { extname } from 'node:path';
import sanitize from 'sanitize-filename';
import { AccessCore, Permission } from '../access';
import { AuthDto } from '../auth';
import { mimeTypes } from '../domain.constant';
import { usePagination } from '../domain.util';
import { IAssetDeletionJob, ISidecarWriteJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
import {
ClientEvent,
IAccessRepository,
IAssetRepository,
IAssetStackRepository,
ICommunicationRepository,
IJobRepository,
IPartnerRepository,
IStorageRepository,
ISystemConfigRepository,
IUserRepository,
JobItem,
JobStatus,
TimeBucketOptions,
} from '../repositories';
import { StorageCore, StorageFolder } from '../storage';
import { SystemConfigCore } from '../system-config';
import {
AssetBulkDeleteDto,
AssetBulkUpdateDto,
AssetJobName,
AssetJobsDto,
AssetStatsDto,
MapMarkerDto,
MemoryLaneDto,
TimeBucketAssetDto,
TimeBucketDto,
UpdateAssetDto,
UpdateStackParentDto,
mapStats,
} from './dto';
import { AccessCore, Permission } from 'src/domain/access/access.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 {
AssetResponseDto,
MapMarkerResponseDto,
MemoryLaneResponseDto,
SanitizedAssetResponseDto,
TimeBucketResponseDto,
mapAsset,
} from './response-dto';
} 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 { usePagination } from 'src/domain/domain.util';
import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants';
import { IAssetDeletionJob, ISidecarWriteJob } from 'src/domain/job/job.interface';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IAssetStackRepository } from 'src/domain/repositories/asset-stack.repository';
import { IAssetRepository, TimeBucketOptions } from 'src/domain/repositories/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { IJobRepository, JobItem, JobStatus } from 'src/domain/repositories/job.repository';
import { IPartnerRepository } from 'src/domain/repositories/partner.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { StorageCore, StorageFolder } from 'src/domain/storage/storage.core';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetEntity } from 'src/infra/entities/asset.entity';
import { LibraryType } from 'src/infra/entities/library.entity';
import { ImmichLogger } from 'src/infra/logger';
export enum UploadFieldName {
ASSET_DATA = 'assetData',

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum } from 'class-validator';
import { ValidateUUID } from '../../domain.util';
import { ValidateUUID } from 'src/domain/domain.util';
export class AssetIdsDto {
@ValidateUUID({ each: true })

View File

@ -1,4 +1,4 @@
import { ValidateUUID } from '../../domain.util';
import { ValidateUUID } from 'src/domain/domain.util';
export class UpdateStackParentDto {
@ValidateUUID()

View File

@ -1,7 +1,7 @@
import { AssetType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { ValidateBoolean } from '../../domain.util';
import { AssetStats } from '../../repositories';
import { ValidateBoolean } from 'src/domain/domain.util';
import { AssetStats } from 'src/domain/repositories/asset.repository';
import { AssetType } from 'src/infra/entities/asset.entity';
export class AssetStatsDto {
@ValidateBoolean({ optional: true })

View File

@ -9,8 +9,8 @@ import {
IsString,
ValidateIf,
} from 'class-validator';
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
import { BulkIdsDto } from '../response-dto';
import { BulkIdsDto } from 'src/domain/asset/response-dto/asset-ids-response.dto';
import { Optional, ValidateBoolean, ValidateUUID } from 'src/domain/domain.util';
export class DeviceIdDto {
@IsNotEmpty()

View File

@ -1,7 +0,0 @@
export * from './asset-ids.dto';
export * from './asset-stack.dto';
export * from './asset-statistics.dto';
export * from './asset.dto';
export * from './map-marker.dto';
export * from './memory-lane.dto';
export * from './time-bucket.dto';

View File

@ -1,4 +1,4 @@
import { ValidateBoolean, ValidateDate } from '../../domain.util';
import { ValidateBoolean, ValidateDate } from 'src/domain/domain.util';
export class MapMarkerDto {
@ValidateBoolean({ optional: true })

View File

@ -1,8 +1,8 @@
import { AssetOrder } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
import { TimeBucketSize } from '../../repositories';
import { Optional, ValidateBoolean, ValidateUUID } from 'src/domain/domain.util';
import { TimeBucketSize } from 'src/domain/repositories/asset.repository';
import { AssetOrder } from 'src/infra/entities/album.entity';
export class TimeBucketDto {
@IsNotEmpty()

View File

@ -1,3 +0,0 @@
export * from './asset.service';
export * from './dto';
export * from './response-dto';

View File

@ -1,4 +1,4 @@
import { ValidateUUID } from '../../domain.util';
import { ValidateUUID } from 'src/domain/domain.util';
/** @deprecated Use `BulkIdResponseDto` instead */
export enum AssetIdErrorReason {

View File

@ -1,11 +1,12 @@
import { AuthDto } from '@app/domain/auth/auth.dto';
import { AssetEntity, AssetFaceEntity, AssetType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { PersonWithFacesResponseDto, mapFacesWithoutPerson, mapPerson } from '../../person/person.dto';
import { TagResponseDto, mapTag } from '../../tag';
import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto';
import { ExifResponseDto, mapExif } from './exif-response.dto';
import { SmartInfoResponseDto, mapSmartInfo } from './smart-info-response.dto';
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 { AssetFaceEntity } from 'src/infra/entities/asset-face.entity';
import { AssetEntity, AssetType } from 'src/infra/entities/asset.entity';
export class SanitizedAssetResponseDto {
id!: string;

View File

@ -1,5 +1,5 @@
import { ExifEntity } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { ExifEntity } from 'src/infra/entities/exif.entity';
export class ExifResponseDto {
make?: string | null = null;

View File

@ -1,6 +0,0 @@
export * from './asset-ids-response.dto';
export * from './asset-response.dto';
export * from './exif-response.dto';
export * from './map-marker-response.dto';
export * from './smart-info-response.dto';
export * from './time-bucket-response.dto';

View File

@ -1,4 +1,4 @@
import { SmartInfoEntity } from '@app/infra/entities';
import { SmartInfoEntity } from 'src/infra/entities/smart-info.entity';
export class SmartInfoResponseDto {
tags?: string[] | null;

View File

@ -1,8 +1,9 @@
import { AssetPathType, EntityType, PathType, PersonPathType, UserPathType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsArray, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator';
import { Optional, ValidateDate, ValidateUUID } from '../domain.util';
import { Optional, ValidateDate, ValidateUUID } from 'src/domain/domain.util';
import { EntityType } from 'src/infra/entities/audit.entity';
import { AssetPathType, PathType, PersonPathType, UserPathType } from 'src/infra/entities/move.entity';
const PathEnum = Object.values({ ...AssetPathType, ...PersonPathType, ...UserPathType });

View File

@ -1,26 +1,21 @@
import { DatabaseAction, EntityType } from '@app/infra/entities';
import {
IAccessRepositoryMock,
auditStub,
authStub,
newAccessRepositoryMock,
newAssetRepositoryMock,
newAuditRepositoryMock,
newCryptoRepositoryMock,
newPersonRepositoryMock,
newStorageRepositoryMock,
newUserRepositoryMock,
} from '@test';
import {
IAssetRepository,
IAuditRepository,
ICryptoRepository,
IPersonRepository,
IStorageRepository,
IUserRepository,
JobStatus,
} from '../repositories';
import { AuditService } from './audit.service';
import { AuditService } from 'src/domain/audit/audit.service';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IAuditRepository } from 'src/domain/repositories/audit.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { JobStatus } from 'src/domain/repositories/job.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { DatabaseAction, EntityType } from 'src/infra/entities/audit.entity';
import { auditStub } from 'test/fixtures/audit.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newAuditRepositoryMock } from 'test/repositories/audit.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
describe(AuditService.name, () => {
let sut: AuditService;

View File

@ -1,24 +1,7 @@
import { AssetPathType, DatabaseAction, PersonPathType, UserPathType } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { DateTime } from 'luxon';
import { resolve } from 'node:path';
import { AccessCore, Permission } from '../access';
import { AuthDto } from '../auth';
import { AUDIT_LOG_MAX_DURATION } from '../domain.constant';
import { usePagination } from '../domain.util';
import { JOBS_ASSET_PAGINATION_SIZE } from '../job';
import {
IAccessRepository,
IAssetRepository,
IAuditRepository,
ICryptoRepository,
IPersonRepository,
IStorageRepository,
IUserRepository,
JobStatus,
} from '../repositories';
import { StorageCore, StorageFolder } from '../storage';
import { AccessCore, Permission } from 'src/domain/access/access.core';
import {
AuditDeletesDto,
AuditDeletesResponseDto,
@ -26,7 +9,23 @@ import {
FileChecksumResponseDto,
FileReportItemDto,
PathEntityType,
} from './audit.dto';
} 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 { usePagination } from 'src/domain/domain.util';
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IAuditRepository } from 'src/domain/repositories/audit.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { JobStatus } from 'src/domain/repositories/job.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { StorageCore, StorageFolder } from 'src/domain/storage/storage.core';
import { DatabaseAction } from 'src/infra/entities/audit.entity';
import { AssetPathType, PersonPathType, UserPathType } from 'src/infra/entities/move.entity';
import { ImmichLogger } from 'src/infra/logger';
@Injectable()
export class AuditService {

View File

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

View File

@ -1,7 +1,10 @@
import { APIKeyEntity, SharedLinkEntity, UserEntity, UserTokenEntity } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
import { APIKeyEntity } from 'src/infra/entities/api-key.entity';
import { SharedLinkEntity } from 'src/infra/entities/shared-link.entity';
import { UserTokenEntity } from 'src/infra/entities/user-token.entity';
import { UserEntity } from 'src/infra/entities/user.entity';
export class AuthDto {
user!: UserEntity;

View File

@ -1,38 +1,32 @@
import { UserEntity } from '@app/infra/entities';
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import {
IAccessRepositoryMock,
authStub,
keyStub,
loginResponseStub,
newAccessRepositoryMock,
newCryptoRepositoryMock,
newKeyRepositoryMock,
newLibraryRepositoryMock,
newSharedLinkRepositoryMock,
newSystemConfigRepositoryMock,
newUserRepositoryMock,
newUserTokenRepositoryMock,
sharedLinkStub,
systemConfigStub,
userStub,
userTokenStub,
} from '@test';
import { IncomingHttpHeaders } from 'node:http';
import { Issuer, generators } from 'openid-client';
import { Socket } from 'socket.io';
import {
ICryptoRepository,
IKeyRepository,
ILibraryRepository,
ISharedLinkRepository,
ISystemConfigRepository,
IUserRepository,
IUserTokenRepository,
} from '../repositories';
import { AuthType } from './auth.constant';
import { AuthDto, SignUpDto } from './auth.dto';
import { AuthService } from './auth.service';
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 { IKeyRepository } from 'src/domain/repositories/api-key.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { ILibraryRepository } from 'src/domain/repositories/library.repository';
import { ISharedLinkRepository } from 'src/domain/repositories/shared-link.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { IUserTokenRepository } from 'src/domain/repositories/user-token.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { UserEntity } from 'src/infra/entities/user.entity';
import { keyStub } from 'test/fixtures/api-key.stub';
import { authStub, loginResponseStub } from 'test/fixtures/auth.stub';
import { sharedLinkStub } from 'test/fixtures/shared-link.stub';
import { systemConfigStub } from 'test/fixtures/system-config.stub';
import { userTokenStub } from 'test/fixtures/user-token.stub';
import { userStub } from 'test/fixtures/user.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock';
import { newSharedLinkRepositoryMock } from 'test/repositories/shared-link.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
import { newUserTokenRepositoryMock } from 'test/repositories/user-token.repository.mock';
import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
// const token = Buffer.from('my-api-key', 'utf8').toString('base64');

View File

@ -1,5 +1,3 @@
import { SystemConfig, UserEntity } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import {
BadRequestException,
Inject,
@ -12,20 +10,7 @@ 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 '../access';
import { HumanReadableSize } from '../domain.util';
import {
IAccessRepository,
ICryptoRepository,
IKeyRepository,
ILibraryRepository,
ISharedLinkRepository,
ISystemConfigRepository,
IUserRepository,
IUserTokenRepository,
} from '../repositories';
import { SystemConfigCore } from '../system-config/system-config.core';
import { UserCore, UserResponseDto, mapUser } from '../user';
import { AccessCore, Permission } from 'src/domain/access/access.core';
import {
AuthType,
IMMICH_ACCESS_COOKIE,
@ -34,7 +19,7 @@ import {
IMMICH_IS_AUTHENTICATED,
LOGIN_URL,
MOBILE_REDIRECT,
} from './auth.constant';
} from 'src/domain/auth/auth.constant';
import {
AuthDeviceResponseDto,
AuthDto,
@ -48,7 +33,22 @@ import {
SignUpDto,
mapLoginResponse,
mapUserToken,
} from './auth.dto';
} from 'src/domain/auth/auth.dto';
import { HumanReadableSize } from 'src/domain/domain.util';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IKeyRepository } from 'src/domain/repositories/api-key.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { ILibraryRepository } from 'src/domain/repositories/library.repository';
import { ISharedLinkRepository } from 'src/domain/repositories/shared-link.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { IUserTokenRepository } from 'src/domain/repositories/user-token.repository';
import { IUserRepository } from 'src/domain/repositories/user.repository';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { UserResponseDto, mapUser } from 'src/domain/user/response-dto/user-response.dto';
import { UserCore } from 'src/domain/user/user.core';
import { SystemConfig } from 'src/infra/entities/system-config.entity';
import { UserEntity } from 'src/infra/entities/user.entity';
import { ImmichLogger } from 'src/infra/logger';
export interface LoginDetails {
isSecure: boolean;

View File

@ -1,3 +0,0 @@
export * from './auth.constant';
export * from './auth.dto';
export * from './auth.service';

View File

@ -1,13 +1,8 @@
import {
DatabaseExtension,
DatabaseService,
IDatabaseRepository,
VectorIndex,
Version,
VersionType,
} from '@app/domain';
import { ImmichLogger } from '@app/infra/logger';
import { newDatabaseRepositoryMock } from '@test';
import { DatabaseService } from 'src/domain/database/database.service';
import { Version, VersionType } from 'src/domain/domain.constant';
import { DatabaseExtension, IDatabaseRepository, VectorIndex } from 'src/domain/repositories/database.repository';
import { ImmichLogger } from 'src/infra/logger';
import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock';
describe(DatabaseService.name, () => {
let sut: DatabaseService;

View File

@ -1,6 +1,5 @@
import { ImmichLogger } from '@app/infra/logger';
import { Inject, Injectable } from '@nestjs/common';
import { Version, VersionType } from '../domain.constant';
import { Version, VersionType } from 'src/domain/domain.constant';
import {
DatabaseExtension,
DatabaseLock,
@ -8,7 +7,8 @@ import {
VectorExtension,
VectorIndex,
extName,
} from '../repositories';
} from 'src/domain/repositories/database.repository';
import { ImmichLogger } from 'src/infra/logger';
@Injectable()
export class DatabaseService {

View File

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

View File

@ -1,7 +1,7 @@
// TODO: remove nestjs references from domain
import { LogLevel } from '@app/infra/entities';
import { ConfigModuleOptions } from '@nestjs/config';
import Joi from 'joi';
import { LogLevel } from 'src/infra/entities/system-config.entity';
const WHEN_DB_URL_SET = Joi.when('DB_URL', {
is: Joi.exist(),

View File

@ -1,4 +1,4 @@
import { Version, VersionType, mimeTypes } from './domain.constant';
import { mimeTypes, Version, VersionType } from 'src/domain/domain.constant';
describe('mimeTypes', () => {
for (const { mimetype, extension } of [

View File

@ -1,7 +1,7 @@
import { AssetType } from '@app/infra/entities';
import { Duration } from 'luxon';
import { readFileSync } from 'node:fs';
import { extname, join } from 'node:path';
import { AssetType } from 'src/infra/entities/asset.entity';
export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 });
export const ONE_HOUR = Duration.fromObject({ hours: 1 });

View File

@ -1,29 +1,29 @@
import { ImmichLogger } from '@app/infra/logger';
import { Global, Module, Provider } from '@nestjs/common';
import { ActivityService } from './activity';
import { AlbumService } from './album';
import { APIKeyService } from './api-key';
import { AssetService } from './asset';
import { AuditService } from './audit';
import { AuthService } from './auth';
import { DatabaseService } from './database';
import { DownloadService } from './download';
import { JobService } from './job';
import { LibraryService } from './library';
import { MediaService } from './media';
import { MetadataService } from './metadata';
import { PartnerService } from './partner';
import { PersonService } from './person';
import { SearchService } from './search';
import { ServerInfoService } from './server-info';
import { SharedLinkService } from './shared-link';
import { SmartInfoService } from './smart-info';
import { StorageService } from './storage';
import { StorageTemplateService } from './storage-template';
import { SystemConfigService } from './system-config';
import { TagService } from './tag';
import { TrashService } from './trash';
import { UserService } from './user';
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,

View File

@ -1,4 +1,3 @@
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, applyDecorators } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
@ -18,6 +17,7 @@ import { CronJob } from 'cron';
import _ from 'lodash';
import { basename, extname } from 'node:path';
import sanitize from 'sanitize-filename';
import { ImmichLogger } from 'src/infra/logger';
export enum CacheControl {
PRIVATE_WITH_CACHE = 'private_with_cache',

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsInt, IsPositive } from 'class-validator';
import { Optional, ValidateUUID } from '../domain.util';
import { Optional, ValidateUUID } from 'src/domain/domain.util';
export class DownloadInfoDto {
@ValidateUUID({ each: true, optional: true })

View File

@ -1,18 +1,16 @@
import { BadRequestException } from '@nestjs/common';
import {
IAccessRepositoryMock,
assetStub,
authStub,
newAccessRepositoryMock,
newAssetRepositoryMock,
newStorageRepositoryMock,
} from '@test';
import { when } from 'jest-when';
import { CacheControl, ImmichFileResponse } from 'src/domain/domain.util';
import { DownloadResponseDto } from 'src/domain/download/download.dto';
import { DownloadService } from 'src/domain/download/download.service';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { Readable } from 'typeorm/platform/PlatformTools.js';
import { CacheControl, ImmichFileResponse } from '../domain.util';
import { IAssetRepository, IStorageRepository } from '../repositories';
import { DownloadResponseDto } from './download.dto';
import { DownloadService } from './download.service';
const downloadResponse: DownloadResponseDto = {
totalSize: 105_000,

View File

@ -1,13 +1,15 @@
import { AssetEntity } from '@app/infra/entities';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { parse } from 'node:path';
import { AccessCore, Permission } from '../access';
import { AssetIdsDto } from '../asset';
import { AuthDto } from '../auth';
import { mimeTypes } from '../domain.constant';
import { CacheControl, HumanReadableSize, ImmichFileResponse, usePagination } from '../domain.util';
import { IAccessRepository, IAssetRepository, IStorageRepository, ImmichReadStream } from '../repositories';
import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from './download.dto';
import { AccessCore, Permission } from 'src/domain/access/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 { CacheControl, HumanReadableSize, ImmichFileResponse, usePagination } from 'src/domain/domain.util';
import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/domain/download/download.dto';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { IStorageRepository, ImmichReadStream } from 'src/domain/repositories/storage.repository';
import { AssetEntity } from 'src/infra/entities/asset.entity';
@Injectable()
export class DownloadService {

View File

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

View File

@ -1,30 +0,0 @@
export * from './access';
export * from './activity';
export * from './album';
export * from './api-key';
export * from './asset';
export * from './audit';
export * from './auth';
export * from './database';
export * from './domain.config';
export * from './domain.constant';
export * from './domain.module';
export * from './domain.util';
export * from './download';
export * from './job';
export * from './library';
export * from './media';
export * from './metadata';
export * from './partner';
export * from './person';
export * from './repositories';
export * from './search';
export * from './server-info';
export * from './shared-link';
export * from './smart-info';
export * from './storage';
export * from './storage-template';
export * from './system-config';
export * from './tag';
export * from './trash';
export * from './user';

View File

@ -1,4 +0,0 @@
export * from './job.constants';
export * from './job.dto';
export * from './job.interface';
export * from './job.service';

View File

@ -1,7 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty } from 'class-validator';
import { ValidateBoolean } from '../domain.util';
import { JobCommand, QueueName } from './job.constants';
import { ValidateBoolean } from 'src/domain/domain.util';
import { JobCommand, QueueName } from 'src/domain/job/job.constants';
export class JobIdParamDto {
@IsNotEmpty()

View File

@ -1,26 +1,19 @@
import { SystemConfig, SystemConfigKey } from '@app/infra/entities';
import { BadRequestException } from '@nestjs/common';
import {
assetStub,
newAssetRepositoryMock,
newCommunicationRepositoryMock,
newJobRepositoryMock,
newPersonRepositoryMock,
newSystemConfigRepositoryMock,
} from '@test';
import {
IAssetRepository,
ICommunicationRepository,
IJobRepository,
IPersonRepository,
ISystemConfigRepository,
JobHandler,
JobItem,
JobStatus,
} from '../repositories';
import { FeatureFlag, SystemConfigCore } from '../system-config/system-config.core';
import { JobCommand, JobName, QueueName } from './job.constants';
import { JobService } from './job.service';
import { JobCommand, JobName, QueueName } from 'src/domain/job/job.constants';
import { JobService } from 'src/domain/job/job.service';
import { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { IJobRepository, JobHandler, JobItem, JobStatus } from 'src/domain/repositories/job.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { FeatureFlag, SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { SystemConfig, SystemConfigKey } from 'src/infra/entities/system-config.entity';
import { assetStub } from 'test/fixtures/asset.stub';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
const makeMockHandlers = (status: JobStatus) => {
const mock = jest.fn().mockResolvedValue(status);

View File

@ -1,22 +1,15 @@
import { AssetType } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { mapAsset } from '../asset';
import {
ClientEvent,
IAssetRepository,
ICommunicationRepository,
IJobRepository,
IPersonRepository,
ISystemConfigRepository,
JobHandler,
JobItem,
JobStatus,
QueueCleanType,
} from '../repositories';
import { FeatureFlag, SystemConfigCore } from '../system-config/system-config.core';
import { ConcurrentQueueName, JobCommand, JobName, QueueName } from './job.constants';
import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto } from './job.dto';
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 { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { IJobRepository, JobHandler, JobItem, JobStatus, QueueCleanType } from 'src/domain/repositories/job.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { FeatureFlag, SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetType } from 'src/infra/entities/asset.entity';
import { ImmichLogger } from 'src/infra/logger';
@Injectable()
export class JobService {

View File

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

View File

@ -1,7 +1,7 @@
import { LibraryEntity, LibraryType } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { ArrayMaxSize, ArrayUnique, IsEnum, IsNotEmpty, IsString } from 'class-validator';
import { Optional, ValidateBoolean, ValidateUUID } from '../domain.util';
import { Optional, ValidateBoolean, ValidateUUID } from 'src/domain/domain.util';
import { LibraryEntity, LibraryType } from 'src/infra/entities/library.entity';
export class CreateLibraryDto {
@IsEnum(LibraryType)

View File

@ -1,45 +1,39 @@
import { AssetType, LibraryType, SystemConfig, SystemConfigKey, UserEntity } from '@app/infra/entities';
import { BadRequestException } from '@nestjs/common';
import {
IAccessRepositoryMock,
assetStub,
authStub,
libraryStub,
makeMockWatcher,
newAccessRepositoryMock,
newAssetRepositoryMock,
newCryptoRepositoryMock,
newDatabaseRepositoryMock,
newJobRepositoryMock,
newLibraryRepositoryMock,
newStorageRepositoryMock,
newSystemConfigRepositoryMock,
systemConfigStub,
userStub,
} from '@test';
import { when } from 'jest-when';
import { R_OK } from 'node:constants';
import { Stats } from 'node:fs';
import { ILibraryFileJob, ILibraryRefreshJob, JobName } from '../job';
import {
IAssetRepository,
ICryptoRepository,
IDatabaseRepository,
IJobRepository,
ILibraryRepository,
IStorageRepository,
ISystemConfigRepository,
JobStatus,
StorageEventType,
} from '../repositories';
import { SystemConfigCore } from '../system-config/system-config.core';
import { mapLibrary } from './library.dto';
import { LibraryService } from './library.service';
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 { IAssetRepository } from 'src/domain/repositories/asset.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IDatabaseRepository } from 'src/domain/repositories/database.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { ILibraryRepository } from 'src/domain/repositories/library.repository';
import { IStorageRepository, StorageEventType } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetType } from 'src/infra/entities/asset.entity';
import { LibraryType } from 'src/infra/entities/library.entity';
import { SystemConfig, SystemConfigKey } from 'src/infra/entities/system-config.entity';
import { UserEntity } from 'src/infra/entities/user.entity';
import { assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { libraryStub } from 'test/fixtures/library.stub';
import { systemConfigStub } from 'test/fixtures/system-config.stub';
import { userStub } from 'test/fixtures/user.stub';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock';
import { makeMockWatcher, newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
describe(LibraryService.name, () => {
let sut: LibraryService;
let accessMock: IAccessRepositoryMock;
let assetMock: jest.Mocked<IAssetRepository>;
let configMock: jest.Mocked<ISystemConfigRepository>;
let cryptoMock: jest.Mocked<ICryptoRepository>;
@ -49,7 +43,6 @@ describe(LibraryService.name, () => {
let databaseMock: jest.Mocked<IDatabaseRepository>;
beforeEach(() => {
accessMock = newAccessRepositoryMock();
configMock = newSystemConfigRepositoryMock();
libraryMock = newLibraryRepositoryMock();
assetMock = newAssetRepositoryMock();
@ -58,19 +51,7 @@ describe(LibraryService.name, () => {
storageMock = newStorageRepositoryMock();
databaseMock = newDatabaseRepositoryMock();
// Always validate owner access for library.
accessMock.library.checkOwnerAccess.mockImplementation((_, libraryIds) => Promise.resolve(libraryIds));
sut = new LibraryService(
accessMock,
assetMock,
configMock,
cryptoMock,
jobMock,
libraryMock,
storageMock,
databaseMock,
);
sut = new LibraryService(assetMock, configMock, cryptoMock, jobMock, libraryMock, storageMock, databaseMock);
databaseMock.tryLock.mockResolvedValue(true);
});

View File

@ -1,5 +1,3 @@
import { AssetType, LibraryEntity, LibraryType } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { Trie } from 'mnemonist';
@ -8,28 +6,10 @@ import { EventEmitter } from 'node:events';
import { Stats } from 'node:fs';
import path, { basename, parse } from 'node:path';
import picomatch from 'picomatch';
import { AccessCore } from '../access';
import { mimeTypes } from '../domain.constant';
import { handlePromiseError, usePagination, validateCronExpression } from '../domain.util';
import { IBaseJob, IEntityJob, ILibraryFileJob, ILibraryRefreshJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
import {
DatabaseLock,
IAccessRepository,
IAssetRepository,
ICryptoRepository,
IDatabaseRepository,
IJobRepository,
ILibraryRepository,
IStorageRepository,
ISystemConfigRepository,
InternalEvent,
InternalEventMap,
JobStatus,
StorageEventType,
WithProperty,
} from '../repositories';
import { StorageCore } from '../storage';
import { SystemConfigCore } from '../system-config';
import { mimeTypes } from 'src/domain/domain.constant';
import { handlePromiseError, usePagination, validateCronExpression } from 'src/domain/domain.util';
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,
@ -41,21 +21,32 @@ import {
ValidateLibraryImportPathResponseDto,
ValidateLibraryResponseDto,
mapLibrary,
} from './library.dto';
} from 'src/domain/library/library.dto';
import { IAssetRepository, WithProperty } from 'src/domain/repositories/asset.repository';
import { InternalEvent, InternalEventMap } from 'src/domain/repositories/communication.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { DatabaseLock, IDatabaseRepository } from 'src/domain/repositories/database.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { ILibraryRepository } from 'src/domain/repositories/library.repository';
import { IStorageRepository, StorageEventType } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { StorageCore } from 'src/domain/storage/storage.core';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetType } from 'src/infra/entities/asset.entity';
import { LibraryEntity, LibraryType } from 'src/infra/entities/library.entity';
import { ImmichLogger } from 'src/infra/logger';
const LIBRARY_SCAN_BATCH_SIZE = 5000;
@Injectable()
export class LibraryService extends EventEmitter {
readonly logger = new ImmichLogger(LibraryService.name);
private access: AccessCore;
private configCore: SystemConfigCore;
private watchLibraries = false;
private watchLock = false;
private watchers: Record<string, () => Promise<void>> = {};
constructor(
@Inject(IAccessRepository) accessRepository: IAccessRepository,
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
@ -65,7 +56,6 @@ export class LibraryService extends EventEmitter {
@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository,
) {
super();
this.access = AccessCore.create(accessRepository);
this.configCore = SystemConfigCore.create(configRepository);
}

View File

@ -1,2 +0,0 @@
export * from './media.constant';
export * from './media.service';

View File

@ -1,43 +1,37 @@
import { Stats } from 'node:fs';
import { JobName } from 'src/domain/job/job.constants';
import { MediaService } from 'src/domain/media/media.service';
import { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { IMediaRepository } from 'src/domain/repositories/media.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { AssetType } from 'src/infra/entities/asset.entity';
import { ExifEntity } from 'src/infra/entities/exif.entity';
import {
AssetType,
AudioCodec,
Colorspace,
ExifEntity,
SystemConfigKey,
ToneMapping,
TranscodeHWAccel,
TranscodePolicy,
VideoCodec,
} from '@app/infra/entities';
import {
assetStub,
faceStub,
newAssetRepositoryMock,
newCryptoRepositoryMock,
newJobRepositoryMock,
newMediaRepositoryMock,
newMoveRepositoryMock,
newPersonRepositoryMock,
newStorageRepositoryMock,
newSystemConfigRepositoryMock,
personStub,
probeStub,
} from '@test';
import { Stats } from 'node:fs';
import { JobName } from '../job';
import {
IAssetRepository,
ICryptoRepository,
IJobRepository,
IMediaRepository,
IMoveRepository,
IPersonRepository,
IStorageRepository,
ISystemConfigRepository,
JobStatus,
WithoutProperty,
} from '../repositories';
import { MediaService } from './media.service';
} from 'src/infra/entities/system-config.entity';
import { assetStub } from 'test/fixtures/asset.stub';
import { faceStub } from 'test/fixtures/face.stub';
import { probeStub } from 'test/fixtures/media.stub';
import { personStub } from 'test/fixtures/person.stub';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock';
import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
describe(MediaService.name, () => {
let sut: MediaService;

View File

@ -1,37 +1,7 @@
import {
AssetEntity,
AssetPathType,
AssetType,
AudioCodec,
Colorspace,
TranscodeHWAccel,
TranscodePolicy,
TranscodeTarget,
VideoCodec,
} from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common';
import { usePagination } from '../domain.util';
import { IBaseJob, IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from '../job';
import {
AudioStreamInfo,
IAssetRepository,
ICryptoRepository,
IJobRepository,
IMediaRepository,
IMoveRepository,
IPersonRepository,
IStorageRepository,
ISystemConfigRepository,
JobItem,
JobStatus,
VideoCodecHWConfig,
VideoStreamInfo,
WithoutProperty,
} from '../repositories';
import { StorageCore, StorageFolder } from '../storage';
import { SystemConfigFFmpegDto } from '../system-config';
import { SystemConfigCore } from '../system-config/system-config.core';
import { usePagination } from 'src/domain/domain.util';
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,
@ -41,7 +11,34 @@ import {
ThumbnailConfig,
VAAPIConfig,
VP9Config,
} from './media.util';
} from 'src/domain/media/media.util';
import { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IJobRepository, JobItem, JobStatus } from 'src/domain/repositories/job.repository';
import {
AudioStreamInfo,
IMediaRepository,
VideoCodecHWConfig,
VideoStreamInfo,
} from 'src/domain/repositories/media.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { StorageCore, StorageFolder } from 'src/domain/storage/storage.core';
import { SystemConfigFFmpegDto } from 'src/domain/system-config/dto/system-config-ffmpeg.dto';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetEntity, AssetType } from 'src/infra/entities/asset.entity';
import { AssetPathType } from 'src/infra/entities/move.entity';
import {
AudioCodec,
Colorspace,
TranscodeHWAccel,
TranscodePolicy,
TranscodeTarget,
VideoCodec,
} from 'src/infra/entities/system-config.entity';
import { ImmichLogger } from 'src/infra/logger';
@Injectable()
export class MediaService {
@ -58,7 +55,7 @@ export class MediaService {
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
@Inject(IMoveRepository) moveRepository: IMoveRepository,
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
) {
this.configCore = SystemConfigCore.create(configRepository);
this.storageCore = StorageCore.create(

View File

@ -1,4 +1,3 @@
import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from '@app/infra/entities';
import {
AudioStreamInfo,
BitrateDistribution,
@ -6,8 +5,16 @@ import {
VideoCodecHWConfig,
VideoCodecSWConfig,
VideoStreamInfo,
} from '../repositories';
import { SystemConfigFFmpegDto } from '../system-config/dto';
} from 'src/domain/repositories/media.repository';
import { SystemConfigFFmpegDto } from 'src/domain/system-config/dto/system-config-ffmpeg.dto';
import {
CQMode,
ToneMapping,
TranscodeHWAccel,
TranscodeTarget,
VideoCodec,
} from 'src/infra/entities/system-config.entity';
class BaseConfig implements VideoCodecSWConfig {
presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast'];
constructor(protected config: SystemConfigFFmpegDto) {}

View File

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

View File

@ -1,46 +1,40 @@
import { AssetType, ExifEntity, SystemConfigKey } from '@app/infra/entities';
import {
assetStub,
fileStub,
newAlbumRepositoryMock,
newAssetRepositoryMock,
newCommunicationRepositoryMock,
newCryptoRepositoryMock,
newDatabaseRepositoryMock,
newJobRepositoryMock,
newMediaRepositoryMock,
newMetadataRepositoryMock,
newMoveRepositoryMock,
newPersonRepositoryMock,
newStorageRepositoryMock,
newSystemConfigRepositoryMock,
probeStub,
} from '@test';
import { BinaryField } from 'exiftool-vendored';
import { when } from 'jest-when';
import { randomBytes } from 'node:crypto';
import { Stats } from 'node:fs';
import { constants } from 'node:fs/promises';
import { JobName } from '../job';
import {
ClientEvent,
IAlbumRepository,
IAssetRepository,
ICommunicationRepository,
ICryptoRepository,
IDatabaseRepository,
IJobRepository,
IMediaRepository,
IMetadataRepository,
IMoveRepository,
IPersonRepository,
IStorageRepository,
ISystemConfigRepository,
ImmichTags,
JobStatus,
WithoutProperty,
} from '../repositories';
import { MetadataService, Orientation } from './metadata.service';
import { JobName } from 'src/domain/job/job.constants';
import { MetadataService, Orientation } from 'src/domain/metadata/metadata.service';
import { IAlbumRepository } from 'src/domain/repositories/album.repository';
import { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IDatabaseRepository } from 'src/domain/repositories/database.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { IMediaRepository } from 'src/domain/repositories/media.repository';
import { IMetadataRepository, ImmichTags } from 'src/domain/repositories/metadata.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { AssetType } from 'src/infra/entities/asset.entity';
import { ExifEntity } from 'src/infra/entities/exif.entity';
import { SystemConfigKey } from 'src/infra/entities/system-config.entity';
import { assetStub } from 'test/fixtures/asset.stub';
import { fileStub } from 'test/fixtures/file.stub';
import { probeStub } from 'test/fixtures/media.stub';
import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCommunicationRepositoryMock } from 'test/repositories/communication.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock';
import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock';
import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
describe(MetadataService.name, () => {
let albumMock: jest.Mocked<IAlbumRepository>;

View File

@ -1,5 +1,3 @@
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
import { Inject, Injectable } from '@nestjs/common';
import { ExifDateTime, Tags } from 'exiftool-vendored';
import { firstDateTime } from 'exiftool-vendored/dist/FirstDateTime';
@ -8,29 +6,26 @@ import { Duration } from 'luxon';
import { constants } from 'node:fs/promises';
import path from 'node:path';
import { Subscription } from 'rxjs';
import { handlePromiseError, usePagination } from '../domain.util';
import { IBaseJob, IEntityJob, ISidecarWriteJob, JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from '../job';
import {
ClientEvent,
DatabaseLock,
IAlbumRepository,
IAssetRepository,
ICommunicationRepository,
ICryptoRepository,
IDatabaseRepository,
IJobRepository,
IMediaRepository,
IMetadataRepository,
IMoveRepository,
IPersonRepository,
IStorageRepository,
ISystemConfigRepository,
ImmichTags,
JobStatus,
WithoutProperty,
} from '../repositories';
import { StorageCore } from '../storage';
import { FeatureFlag, SystemConfigCore } from '../system-config';
import { handlePromiseError, usePagination } from 'src/domain/domain.util';
import { JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from 'src/domain/job/job.constants';
import { IBaseJob, IEntityJob, ISidecarWriteJob } from 'src/domain/job/job.interface';
import { IAlbumRepository } from 'src/domain/repositories/album.repository';
import { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/domain/repositories/communication.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { DatabaseLock, IDatabaseRepository } from 'src/domain/repositories/database.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { IMediaRepository } from 'src/domain/repositories/media.repository';
import { IMetadataRepository, ImmichTags } from 'src/domain/repositories/metadata.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { StorageCore } from 'src/domain/storage/storage.core';
import { FeatureFlag, SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { AssetEntity, AssetType } from 'src/infra/entities/asset.entity';
import { ExifEntity } from 'src/infra/entities/exif.entity';
import { ImmichLogger } from 'src/infra/logger';
/** look for a date from these tags (in order) */
const EXIF_DATE_TAGS: Array<keyof Tags> = [

View File

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

View File

@ -1,5 +1,5 @@
import { IsNotEmpty } from 'class-validator';
import { UserResponseDto } from '../user';
import { UserResponseDto } from 'src/domain/user/response-dto/user-response.dto';
export class UpdatePartnerDto {
@IsNotEmpty()

View File

@ -1,9 +1,12 @@
import { UserAvatarColor } from '@app/infra/entities';
import { BadRequestException } from '@nestjs/common';
import { authStub, newPartnerRepositoryMock, partnerStub } from '@test';
import { IAccessRepository, IPartnerRepository, PartnerDirection } from '../repositories';
import { PartnerResponseDto } from './partner.dto';
import { PartnerService } from './partner.service';
import { PartnerResponseDto } from 'src/domain/partner/partner.dto';
import { PartnerService } from 'src/domain/partner/partner.service';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IPartnerRepository, PartnerDirection } from 'src/domain/repositories/partner.repository';
import { UserAvatarColor } from 'src/infra/entities/user.entity';
import { authStub } from 'test/fixtures/auth.stub';
import { partnerStub } from 'test/fixtures/partner.stub';
import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock';
const responseDto = {
admin: <PartnerResponseDto>{

View File

@ -1,10 +1,11 @@
import { PartnerEntity } from '@app/infra/entities';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { AccessCore, Permission } from '../access';
import { AuthDto } from '../auth';
import { IAccessRepository, IPartnerRepository, PartnerDirection, PartnerIds } from '../repositories';
import { mapUser } from '../user';
import { PartnerResponseDto, UpdatePartnerDto } from './partner.dto';
import { AccessCore, Permission } from 'src/domain/access/access.core';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { PartnerResponseDto, UpdatePartnerDto } from 'src/domain/partner/partner.dto';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IPartnerRepository, PartnerDirection, PartnerIds } from 'src/domain/repositories/partner.repository';
import { mapUser } from 'src/domain/user/response-dto/user-response.dto';
import { PartnerEntity } from 'src/infra/entities/partner.entity';
@Injectable()
export class PartnerService {

View File

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

View File

@ -1,9 +1,10 @@
import { AssetFaceEntity, PersonEntity } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsArray, IsNotEmpty, IsString, MaxDate, ValidateNested } from 'class-validator';
import { AuthDto } from '../auth';
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from '../domain.util';
import { AuthDto } from 'src/domain/auth/auth.dto';
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/domain/domain.util';
import { AssetFaceEntity } from 'src/infra/entities/asset-face.entity';
import { PersonEntity } from 'src/infra/entities/person.entity';
export class PersonCreateDto {
/**

View File

@ -1,44 +1,37 @@
import { AssetFaceEntity, Colorspace, SystemConfigKey } from '@app/infra/entities';
import { BadRequestException, NotFoundException } from '@nestjs/common';
import {
IAccessRepositoryMock,
assetStub,
authStub,
faceStub,
newAccessRepositoryMock,
newAssetRepositoryMock,
newCryptoRepositoryMock,
newJobRepositoryMock,
newMachineLearningRepositoryMock,
newMediaRepositoryMock,
newMoveRepositoryMock,
newPersonRepositoryMock,
newSearchRepositoryMock,
newStorageRepositoryMock,
newSystemConfigRepositoryMock,
personStub,
} from '@test';
import { BulkIdErrorReason } from 'src/domain/asset/response-dto/asset-ids-response.dto';
import { CacheControl, ImmichFileResponse } from 'src/domain/domain.util';
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 { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IJobRepository, JobStatus } from 'src/domain/repositories/job.repository';
import { IMachineLearningRepository } from 'src/domain/repositories/machine-learning.repository';
import { IMediaRepository } from 'src/domain/repositories/media.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository } from 'src/domain/repositories/person.repository';
import { FaceSearchResult, ISearchRepository } from 'src/domain/repositories/search.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { AssetFaceEntity } from 'src/infra/entities/asset-face.entity';
import { Colorspace, SystemConfigKey } from 'src/infra/entities/system-config.entity';
import { assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { faceStub } from 'test/fixtures/face.stub';
import { personStub } from 'test/fixtures/person.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock';
import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
import { newMachineLearningRepositoryMock } from 'test/repositories/machine-learning.repository.mock';
import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock';
import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock';
import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock';
import { newSearchRepositoryMock } from 'test/repositories/search.repository.mock';
import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock';
import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock';
import { IsNull } from 'typeorm';
import { BulkIdErrorReason } from '../asset';
import { CacheControl, ImmichFileResponse } from '../domain.util';
import { JobName } from '../job';
import {
FaceSearchResult,
IAssetRepository,
ICryptoRepository,
IJobRepository,
IMachineLearningRepository,
IMediaRepository,
IMoveRepository,
IPersonRepository,
ISearchRepository,
IStorageRepository,
ISystemConfigRepository,
JobStatus,
WithoutProperty,
} from '../repositories';
import { PersonResponseDto, mapFaces, mapPerson } from './person.dto';
import { PersonService } from './person.service';
const responseDto: PersonResponseDto = {
id: 'person-1',

View File

@ -1,35 +1,13 @@
import { PersonEntity } from '@app/infra/entities';
import { PersonPathType } from '@app/infra/entities/move.entity';
import { ImmichLogger } from '@app/infra/logger';
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
import { IsNull } from 'typeorm';
import { AccessCore, Permission } from '../access';
import { AssetResponseDto, BulkIdErrorReason, BulkIdResponseDto, mapAsset } from '../asset';
import { AuthDto } from '../auth';
import { mimeTypes } from '../domain.constant';
import { CacheControl, ImmichFileResponse, usePagination } from '../domain.util';
import { IBaseJob, IDeferrableJob, IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from '../job';
import { FACE_THUMBNAIL_SIZE } from '../media';
import {
CropOptions,
IAccessRepository,
IAssetRepository,
ICryptoRepository,
IJobRepository,
IMachineLearningRepository,
IMediaRepository,
IMoveRepository,
IPersonRepository,
ISearchRepository,
IStorageRepository,
ISystemConfigRepository,
JobItem,
JobStatus,
UpdateFacesData,
WithoutProperty,
} from '../repositories';
import { StorageCore } from '../storage';
import { SystemConfigCore } from '../system-config';
import { AccessCore, Permission } from 'src/domain/access/access.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 { CacheControl, ImmichFileResponse, usePagination } from 'src/domain/domain.util';
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 {
AssetFaceResponseDto,
AssetFaceUpdateDto,
@ -44,7 +22,24 @@ import {
PersonUpdateDto,
mapFaces,
mapPerson,
} from './person.dto';
} from 'src/domain/person/person.dto';
import { IAccessRepository } from 'src/domain/repositories/access.repository';
import { IAssetRepository, WithoutProperty } from 'src/domain/repositories/asset.repository';
import { ICryptoRepository } from 'src/domain/repositories/crypto.repository';
import { IJobRepository, JobItem, JobStatus } from 'src/domain/repositories/job.repository';
import { IMachineLearningRepository } from 'src/domain/repositories/machine-learning.repository';
import { CropOptions, IMediaRepository } from 'src/domain/repositories/media.repository';
import { IMoveRepository } from 'src/domain/repositories/move.repository';
import { IPersonRepository, UpdateFacesData } from 'src/domain/repositories/person.repository';
import { ISearchRepository } from 'src/domain/repositories/search.repository';
import { IStorageRepository } from 'src/domain/repositories/storage.repository';
import { ISystemConfigRepository } from 'src/domain/repositories/system-config.repository';
import { StorageCore } from 'src/domain/storage/storage.core';
import { SystemConfigCore } from 'src/domain/system-config/system-config.core';
import { PersonPathType } from 'src/infra/entities/move.entity';
import { PersonEntity } from 'src/infra/entities/person.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IsNull } from 'typeorm';
@Injectable()
export class PersonService {
@ -64,7 +59,7 @@ export class PersonService {
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(ISearchRepository) private smartInfoRepository: ISearchRepository,
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
) {
this.access = AccessCore.create(accessRepository);
this.configCore = SystemConfigCore.create(configRepository);

View File

@ -1,5 +1,5 @@
import { ActivityEntity } from '@app/infra/entities/activity.entity';
import { ActivitySearch } from '@app/infra/repositories';
import { ActivityEntity } from 'src/infra/entities/activity.entity';
import { ActivitySearch } from 'src/infra/repositories/activity.repository';
export const IActivityRepository = 'IActivityRepository';

View File

@ -1,4 +1,4 @@
import { AlbumEntity } from '@app/infra/entities';
import { AlbumEntity } from 'src/infra/entities/album.entity';
export const IAlbumRepository = 'IAlbumRepository';

View File

@ -1,4 +1,4 @@
import { APIKeyEntity } from '@app/infra/entities';
import { APIKeyEntity } from 'src/infra/entities/api-key.entity';
export const IKeyRepository = 'IKeyRepository';

View File

@ -1,4 +1,4 @@
import { AssetStackEntity } from '@app/infra/entities/asset-stack.entity';
import { AssetStackEntity } from 'src/infra/entities/asset-stack.entity';
export const IAssetStackRepository = 'IAssetStackRepository';

View File

@ -1,7 +1,11 @@
import { AssetSearchOptions, ReverseGeocodeResult, SearchExploreItem } from '@app/domain';
import { AssetEntity, AssetJobStatusEntity, AssetOrder, AssetType, ExifEntity } from '@app/infra/entities';
import { Paginated, PaginationOptions } from 'src/domain/domain.util';
import { ReverseGeocodeResult } from 'src/domain/repositories/metadata.repository';
import { AssetSearchOptions, SearchExploreItem } from 'src/domain/repositories/search.repository';
import { AssetOrder } from 'src/infra/entities/album.entity';
import { AssetJobStatusEntity } from 'src/infra/entities/asset-job-status.entity';
import { AssetEntity, AssetType } from 'src/infra/entities/asset.entity';
import { ExifEntity } from 'src/infra/entities/exif.entity';
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm';
import { Paginated, PaginationOptions } from '../domain.util';
export type AssetStats = Record<AssetType, number>;

View File

@ -1,4 +1,4 @@
import { AuditEntity, DatabaseAction, EntityType } from '@app/infra/entities';
import { AuditEntity, DatabaseAction, EntityType } from 'src/infra/entities/audit.entity';
export const IAuditRepository = 'IAuditRepository';

View File

@ -1,5 +1,6 @@
import { AssetResponseDto, ReleaseNotification, ServerVersionResponseDto } from '@app/domain';
import { SystemConfig } from '@app/infra/entities';
import { AssetResponseDto } from 'src/domain/asset/response-dto/asset-response.dto';
import { ReleaseNotification, ServerVersionResponseDto } from 'src/domain/server-info/server-info.dto';
import { SystemConfig } from 'src/infra/entities/system-config.entity';
export const ICommunicationRepository = 'ICommunicationRepository';

View File

@ -1,4 +1,4 @@
import { Version } from '../domain.constant';
import { Version } from 'src/domain/domain.constant';
export enum DatabaseExtension {
CUBE = 'cube',

View File

@ -1,27 +0,0 @@
export * from './access.repository';
export * from './activity.repository';
export * from './album.repository';
export * from './api-key.repository';
export * from './asset-stack.repository';
export * from './asset.repository';
export * from './audit.repository';
export * from './communication.repository';
export * from './crypto.repository';
export * from './database.repository';
export * from './job.repository';
export * from './library.repository';
export * from './machine-learning.repository';
export * from './media.repository';
export * from './metadata.repository';
export * from './move.repository';
export * from './partner.repository';
export * from './person.repository';
export * from './search.repository';
export * from './server-info.repository';
export * from './shared-link.repository';
export * from './storage.repository';
export * from './system-config.repository';
export * from './system-metadata.repository';
export * from './tag.repository';
export * from './user-token.repository';
export * from './user.repository';

View File

@ -1,5 +1,4 @@
import { JobName, QueueName } from '../job/job.constants';
import { JobName, QueueName } from 'src/domain/job/job.constants';
import {
IAssetDeletionJob,
IBaseJob,
@ -9,7 +8,7 @@ import {
ILibraryFileJob,
ILibraryRefreshJob,
ISidecarWriteJob,
} from '../job/job.interface';
} from 'src/domain/job/job.interface';
export interface JobCounts {
active: number;

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