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

refactor: infra folder (#8138)

This commit is contained in:
Jason Rasmussen 2024-03-20 22:15:09 -05:00 committed by GitHub
parent 9fd5d2ad9c
commit 16d0df796c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
139 changed files with 968 additions and 1164 deletions

View File

@ -354,7 +354,7 @@ jobs:
id: verify-changed-sql-files
with:
files: |
server/src/infra/sql
server/src/queries
- name: Verify SQL files have not changed
if: steps.verify-changed-sql-files.outputs.files_changed == 'true'

View File

@ -5,7 +5,7 @@ After making any changes in the `server/src/entities`, a database migration need
1. Run the command
```bash
npm run typeorm:migrations:generate ./src/infra/<migration-name>
npm run typeorm:migrations:generate <migration-name>
```
2. Check if the migration file makes sense.

View File

@ -25,12 +25,12 @@
"e2e:jobs": "jest --config e2e/jobs/jest-e2e.json --runInBand",
"typeorm": "typeorm",
"typeorm:migrations:create": "typeorm migration:create",
"typeorm:migrations:generate": "typeorm migration:generate -d ./dist/infra/database.config.js",
"typeorm:migrations:run": "typeorm migration:run -d ./dist/infra/database.config.js",
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/infra/database.config.js",
"typeorm:schema:drop": "typeorm query -d ./dist/infra/database.config.js 'DROP schema public cascade; CREATE schema public;'",
"typeorm:migrations:generate": "typeorm migration:generate -d ./dist/database.config.js",
"typeorm:migrations:run": "typeorm migration:run -d ./dist/database.config.js",
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/database.config.js",
"typeorm:schema:drop": "typeorm query -d ./dist/database.config.js 'DROP schema public cascade; CREATE schema public;'",
"typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run",
"sql:generate": "node ./dist/infra/sql-generator/"
"sql:generate": "node ./dist/utils/sql.js"
},
"dependencies": {
"@babel/runtime": "^7.22.11",
@ -146,15 +146,16 @@
"^.+\\.ts$": "ts-jest"
},
"collectCoverageFrom": [
"<rootDir>/src/**/*.(t|j)s",
"!<rootDir>/src/infra/**/*",
"!<rootDir>/src/migrations/**/*",
"!<rootDir>/src/subscribers/**/*",
"!<rootDir>/src/immich/controllers/**/*"
"<rootDir>/src/cores/*.(t|j)s",
"<rootDir>/src/dtos/*.(t|j)s",
"<rootDir>/src/interfaces/*.(t|j)s",
"<rootDir>/src/services/*.(t|j)s",
"<rootDir>/src/utils/*.(t|j)s",
"<rootDir>/src/*.t|j)s"
],
"coverageDirectory": "./coverage",
"coverageThreshold": {
"./src/domain/": {
"./src/": {
"branches": 75,
"functions": 80,
"lines": 85,

View File

@ -7,11 +7,11 @@ import sirv from 'sirv';
import { ApiModule } from 'src/apps/api.module';
import { ApiService } from 'src/apps/api.service';
import { excludePaths } from 'src/config';
import { WEB_ROOT, envName, isDev, serverVersion } from 'src/domain/domain.constant';
import { WEB_ROOT, envName, isDev, serverVersion } from 'src/constants';
import { useSwagger } from 'src/immich/app.utils';
import { otelSDK } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { WebSocketAdapter } from 'src/infra/websocket.adapter';
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
import { otelSDK } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
const logger = new ImmichLogger('ImmichServer');
const port = Number(process.env.SERVER_PORT) || 3001;

View File

@ -3,8 +3,7 @@ import { Cron, CronExpression, Interval } from '@nestjs/schedule';
import { NextFunction, Request, Response } from 'express';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { ONE_HOUR, WEB_ROOT } from 'src/domain/domain.constant';
import { ImmichLogger } from 'src/infra/logger';
import { ONE_HOUR, WEB_ROOT } from 'src/constants';
import { AuthService } from 'src/services/auth.service';
import { DatabaseService } from 'src/services/database.service';
import { JobService } from 'src/services/job.service';
@ -12,7 +11,8 @@ import { ServerInfoService } from 'src/services/server-info.service';
import { SharedLinkService } from 'src/services/shared-link.service';
import { StorageService } from 'src/services/storage.service';
import { SystemConfigService } from 'src/services/system-config.service';
import { OpenGraphTags } from 'src/utils';
import { ImmichLogger } from 'src/utils/logger';
import { OpenGraphTags } from 'src/utils/misc';
const render = (index: string, meta: OpenGraphTags) => {
const tags = `

View File

@ -6,10 +6,8 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OpenTelemetryModule } from 'nestjs-otel';
import { bullConfig, bullQueues, immichAppConfig } from 'src/config';
import { databaseConfig } from 'src/database.config';
import { databaseEntities } from 'src/entities';
import { databaseConfig } from 'src/infra/database.config';
import { otelConfig } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IActivityRepository } from 'src/interfaces/activity.repository';
import { IAlbumRepository } from 'src/interfaces/album.repository';
@ -88,6 +86,8 @@ import { SystemConfigService } from 'src/services/system-config.service';
import { TagService } from 'src/services/tag.service';
import { TrashService } from 'src/services/trash.service';
import { UserService } from 'src/services/user.service';
import { otelConfig } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
const services: Provider[] = [
APIKeyService,

View File

@ -1,9 +1,9 @@
import { NestFactory } from '@nestjs/core';
import { MicroservicesModule } from 'src/apps/microservices.module';
import { envName, serverVersion } from 'src/domain/domain.constant';
import { otelSDK } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { WebSocketAdapter } from 'src/infra/websocket.adapter';
import { envName, serverVersion } from 'src/constants';
import { WebSocketAdapter } from 'src/middleware/websocket.adapter';
import { otelSDK } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
const logger = new ImmichLogger('ImmichMicroservice');
const port = Number(process.env.MICROSERVICES_PORT) || 3002;

View File

@ -1,7 +1,5 @@
import { Injectable } from '@nestjs/common';
import { JobName } from 'src/domain/job/job.constants';
import { IDeleteFilesJob } from 'src/domain/job/job.interface';
import { otelSDK } from 'src/infra/instrumentation';
import { IDeleteFilesJob, JobName } from 'src/interfaces/job.repository';
import { AssetService } from 'src/services/asset.service';
import { AuditService } from 'src/services/audit.service';
import { DatabaseService } from 'src/services/database.service';
@ -15,6 +13,7 @@ import { StorageTemplateService } from 'src/services/storage-template.service';
import { StorageService } from 'src/services/storage.service';
import { SystemConfigService } from 'src/services/system-config.service';
import { UserService } from 'src/services/user.service';
import { otelSDK } from 'src/utils/instrumentation';
@Injectable()
export class MicroservicesService {

View File

@ -3,8 +3,8 @@ import { ConfigModuleOptions } from '@nestjs/config';
import { QueueOptions } from 'bullmq';
import { RedisOptions } from 'ioredis';
import Joi from 'joi';
import { QueueName } from 'src/domain/job/job.constants';
import { LogLevel } from 'src/entities/system-config.entity';
import { QueueName } from 'src/interfaces/job.repository';
const WHEN_DB_URL_SET = Joi.when('DB_URL', {
is: Joi.exist(),

105
server/src/constants.ts Normal file
View File

@ -0,0 +1,105 @@
import { Duration } from 'luxon';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { Version } from 'src/utils/version';
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
export const serverVersion = Version.fromString(version);
export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 });
export const ONE_HOUR = Duration.fromObject({ hours: 1 });
export const envName = (process.env.NODE_ENV || 'development').toUpperCase();
export const isDev = process.env.NODE_ENV === 'development';
export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload';
export const WEB_ROOT = process.env.IMMICH_WEB_ROOT || '/usr/src/app/www';
const GEODATA_ROOT_PATH = process.env.IMMICH_REVERSE_GEOCODING_ROOT || '/usr/src/resources';
export const citiesFile = 'cities500.txt';
export const geodataDatePath = join(GEODATA_ROOT_PATH, 'geodata-date.txt');
export const geodataAdmin1Path = join(GEODATA_ROOT_PATH, 'admin1CodesASCII.txt');
export const geodataAdmin2Path = join(GEODATA_ROOT_PATH, 'admin2Codes.txt');
export const geodataCities500Path = join(GEODATA_ROOT_PATH, citiesFile);
export const MOBILE_REDIRECT = 'app.immich:/';
export const LOGIN_URL = '/auth/login?autoLaunch=0';
export const IMMICH_ACCESS_COOKIE = 'immich_access_token';
export const IMMICH_IS_AUTHENTICATED = 'immich_is_authenticated';
export const IMMICH_AUTH_TYPE_COOKIE = 'immich_auth_type';
export const IMMICH_API_KEY_NAME = 'api_key';
export const IMMICH_API_KEY_HEADER = 'x-api-key';
export const IMMICH_SHARED_LINK_ACCESS_COOKIE = 'immich_shared_link_token';
export enum AuthType {
PASSWORD = 'password',
OAUTH = 'oauth',
}
export const FACE_THUMBNAIL_SIZE = 250;
export const supportedYearTokens = ['y', 'yy'];
export const supportedMonthTokens = ['M', 'MM', 'MMM', 'MMMM'];
export const supportedWeekTokens = ['W', 'WW'];
export const supportedDayTokens = ['d', 'dd'];
export const supportedHourTokens = ['h', 'hh', 'H', 'HH'];
export const supportedMinuteTokens = ['m', 'mm'];
export const supportedSecondTokens = ['s', 'ss', 'SSS'];
export const supportedPresetTokens = [
'{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}/{{MM}}-{{dd}}/{{filename}}',
'{{y}}/{{MMMM}}-{{dd}}/{{filename}}',
'{{y}}/{{MM}}/{{filename}}',
'{{y}}/{{MMM}}/{{filename}}',
'{{y}}/{{MMMM}}/{{filename}}',
'{{y}}/{{MM}}/{{dd}}/{{filename}}',
'{{y}}/{{MMMM}}/{{dd}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}-{{MMM}}-{{dd}}/{{filename}}',
'{{y}}-{{MMMM}}-{{dd}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}/{{filename}}',
'{{y}}/{{y}}-{{WW}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}-{{dd}}/{{assetId}}',
'{{y}}/{{y}}-{{MM}}/{{assetId}}',
'{{y}}/{{y}}-{{WW}}/{{assetId}}',
'{{album}}/{{filename}}',
];
type ModelInfo = { dimSize: number };
export const CLIP_MODEL_INFO: Record<string, ModelInfo> = {
RN50__openai: { dimSize: 1024 },
RN50__yfcc15m: { dimSize: 1024 },
RN50__cc12m: { dimSize: 1024 },
RN101__openai: { dimSize: 512 },
RN101__yfcc15m: { dimSize: 512 },
RN50x4__openai: { dimSize: 640 },
RN50x16__openai: { dimSize: 768 },
RN50x64__openai: { dimSize: 1024 },
'ViT-B-32__openai': { dimSize: 512 },
'ViT-B-32__laion2b_e16': { dimSize: 512 },
'ViT-B-32__laion400m_e31': { dimSize: 512 },
'ViT-B-32__laion400m_e32': { dimSize: 512 },
'ViT-B-32__laion2b-s34b-b79k': { dimSize: 512 },
'ViT-B-16__openai': { dimSize: 512 },
'ViT-B-16__laion400m_e31': { dimSize: 512 },
'ViT-B-16__laion400m_e32': { dimSize: 512 },
'ViT-B-16-plus-240__laion400m_e31': { dimSize: 640 },
'ViT-B-16-plus-240__laion400m_e32': { dimSize: 640 },
'ViT-L-14__openai': { dimSize: 768 },
'ViT-L-14__laion400m_e31': { dimSize: 768 },
'ViT-L-14__laion400m_e32': { dimSize: 768 },
'ViT-L-14__laion2b-s32b-b82k': { dimSize: 768 },
'ViT-L-14-336__openai': { dimSize: 768 },
'ViT-L-14-quickgelu__dfn2b': { dimSize: 768 },
'ViT-H-14__laion2b-s32b-b79k': { dimSize: 1024 },
'ViT-H-14-quickgelu__dfn5b': { dimSize: 1024 },
'ViT-H-14-378-quickgelu__dfn5b': { dimSize: 1024 },
'ViT-g-14__laion2b-s12b-b42k': { dimSize: 1024 },
'LABSE-Vit-L-14': { dimSize: 768 },
'XLM-Roberta-Large-Vit-B-32': { dimSize: 512 },
'XLM-Roberta-Large-Vit-B-16Plus': { dimSize: 640 },
'XLM-Roberta-Large-Vit-L-14': { dimSize: 768 },
'XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k': { dimSize: 1024 },
'nllb-clip-base-siglip__v1': { dimSize: 768 },
'nllb-clip-large-siglip__v1': { dimSize: 1152 },
};

View File

@ -1,7 +1,7 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Req, Res } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/domain/auth/auth.constant';
import { IMMICH_ACCESS_COOKIE, IMMICH_AUTH_TYPE_COOKIE, IMMICH_IS_AUTHENTICATED } from 'src/constants';
import {
AuthDeviceResponseDto,
AuthDto,

View File

@ -1,7 +1,7 @@
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/domain/auth/auth.constant';
import { IMMICH_SHARED_LINK_ACCESS_COOKIE } from 'src/constants';
import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto';
import { AssetIdsDto } from 'src/dtos/asset.dto';
import { AuthDto } from 'src/dtos/auth.dto';

View File

@ -2,7 +2,7 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { AuthDto } from 'src/dtos/auth.dto';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { setDifference, setIsEqual, setUnion } from 'src/utils';
import { setDifference, setIsEqual, setUnion } from 'src/utils/set';
export enum Permission {
ACTIVITY_CREATE = 'activity.create',

View File

@ -1,16 +1,16 @@
import { dirname, join, resolve } from 'node:path';
import { APP_MEDIA_LOCATION } from 'src/constants';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { APP_MEDIA_LOCATION } from 'src/domain/domain.constant';
import { AssetEntity } from 'src/entities/asset.entity';
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { ICryptoRepository } from 'src/interfaces/crypto.repository';
import { IMoveRepository } from 'src/interfaces/move.repository';
import { IPersonRepository } from 'src/interfaces/person.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { ImmichLogger } from 'src/utils/logger';
export enum StorageFolder {
ENCODED_VIDEO = 'encoded-video',

View File

@ -5,7 +5,6 @@ import { validate } from 'class-validator';
import { load as loadYaml } from 'js-yaml';
import * as _ from 'lodash';
import { Subject } from 'rxjs';
import { QueueName } from 'src/domain/job/job.constants';
import { SystemConfigDto } from 'src/dtos/system-config.dto';
import {
AudioCodec,
@ -21,8 +20,9 @@ import {
TranscodePolicy,
VideoCodec,
} from 'src/entities/system-config.entity';
import { ImmichLogger } from 'src/infra/logger';
import { QueueName } from 'src/interfaces/job.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { ImmichLogger } from 'src/utils/logger';
export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise<void>;

View File

@ -16,9 +16,9 @@ const urlOrParts = url
/* eslint unicorn/prefer-module: "off" -- We can fix this when migrating to ESM*/
export const databaseConfig: PostgresConnectionOptions = {
type: 'postgres',
entities: [__dirname + '/../entities/*.entity.{js,ts}'],
migrations: [__dirname + '/../migrations/*.{js,ts}'],
subscribers: [__dirname + '/../subscribers/*.{js,ts}'],
entities: [__dirname + '/entities/*.entity.{js,ts}'],
migrations: [__dirname + '/migrations/*.{js,ts}'],
subscribers: [__dirname + '/subscribers/*.{js,ts}'],
migrationsRun: false,
synchronize: false,
connectTimeoutMS: 10_000, // 10 seconds

View File

@ -1,6 +1,6 @@
import { SetMetadata } from '@nestjs/common';
import _ from 'lodash';
import { setUnion } from 'src/utils';
import { setUnion } from 'src/utils/set';
// PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the
// maximum number of parameters is 65535. Any query that tries to bind more than that (e.g. searching

View File

@ -1,12 +0,0 @@
export const MOBILE_REDIRECT = 'app.immich:/';
export const LOGIN_URL = '/auth/login?autoLaunch=0';
export const IMMICH_ACCESS_COOKIE = 'immich_access_token';
export const IMMICH_IS_AUTHENTICATED = 'immich_is_authenticated';
export const IMMICH_AUTH_TYPE_COOKIE = 'immich_auth_type';
export const IMMICH_API_KEY_NAME = 'api_key';
export const IMMICH_API_KEY_HEADER = 'x-api-key';
export const IMMICH_SHARED_LINK_ACCESS_COOKIE = 'immich_shared_link_token';
export enum AuthType {
PASSWORD = 'password',
OAUTH = 'oauth',
}

View File

@ -1,155 +0,0 @@
export enum QueueName {
THUMBNAIL_GENERATION = 'thumbnailGeneration',
METADATA_EXTRACTION = 'metadataExtraction',
VIDEO_CONVERSION = 'videoConversion',
FACE_DETECTION = 'faceDetection',
FACIAL_RECOGNITION = 'facialRecognition',
SMART_SEARCH = 'smartSearch',
BACKGROUND_TASK = 'backgroundTask',
STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
MIGRATION = 'migration',
SEARCH = 'search',
SIDECAR = 'sidecar',
LIBRARY = 'library',
}
export type ConcurrentQueueName = Exclude<
QueueName,
QueueName.STORAGE_TEMPLATE_MIGRATION | QueueName.FACIAL_RECOGNITION
>;
export enum JobCommand {
START = 'start',
PAUSE = 'pause',
RESUME = 'resume',
EMPTY = 'empty',
CLEAR_FAILED = 'clear-failed',
}
export enum JobName {
// conversion
QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
VIDEO_CONVERSION = 'video-conversion',
// thumbnails
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail',
GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail',
GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail',
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
// metadata
QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction',
METADATA_EXTRACTION = 'metadata-extraction',
LINK_LIVE_PHOTOS = 'link-live-photos',
// user
USER_DELETION = 'user-deletion',
USER_DELETE_CHECK = 'user-delete-check',
USER_SYNC_USAGE = 'user-sync-usage',
// asset
ASSET_DELETION = 'asset-deletion',
ASSET_DELETION_CHECK = 'asset-deletion-check',
// storage template
STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration',
STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single',
// migration
QUEUE_MIGRATION = 'queue-migration',
MIGRATE_ASSET = 'migrate-asset',
MIGRATE_PERSON = 'migrate-person',
// facial recognition
PERSON_CLEANUP = 'person-cleanup',
QUEUE_FACE_DETECTION = 'queue-face-detection',
FACE_DETECTION = 'face-detection',
QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition',
FACIAL_RECOGNITION = 'facial-recognition',
// library managment
LIBRARY_SCAN = 'library-refresh',
LIBRARY_SCAN_ASSET = 'library-refresh-asset',
LIBRARY_REMOVE_OFFLINE = 'library-remove-offline',
LIBRARY_DELETE = 'library-delete',
LIBRARY_QUEUE_SCAN_ALL = 'library-queue-all-refresh',
LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
// cleanup
DELETE_FILES = 'delete-files',
CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
// smart search
QUEUE_SMART_SEARCH = 'queue-smart-search',
SMART_SEARCH = 'smart-search',
// XMP sidecars
QUEUE_SIDECAR = 'queue-sidecar',
SIDECAR_DISCOVERY = 'sidecar-discovery',
SIDECAR_SYNC = 'sidecar-sync',
SIDECAR_WRITE = 'sidecar-write',
}
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
// misc
[JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK,
[JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK,
[JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK,
[JobName.USER_DELETION]: QueueName.BACKGROUND_TASK,
[JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK,
[JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK,
[JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK,
[JobName.USER_SYNC_USAGE]: QueueName.BACKGROUND_TASK,
// conversion
[JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
[JobName.VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
// thumbnails
[JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
// metadata
[JobName.QUEUE_METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
[JobName.METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
[JobName.LINK_LIVE_PHOTOS]: QueueName.METADATA_EXTRACTION,
// storage template
[JobName.STORAGE_TEMPLATE_MIGRATION]: QueueName.STORAGE_TEMPLATE_MIGRATION,
[JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: QueueName.STORAGE_TEMPLATE_MIGRATION,
// migration
[JobName.QUEUE_MIGRATION]: QueueName.MIGRATION,
[JobName.MIGRATE_ASSET]: QueueName.MIGRATION,
[JobName.MIGRATE_PERSON]: QueueName.MIGRATION,
// facial recognition
[JobName.QUEUE_FACE_DETECTION]: QueueName.FACE_DETECTION,
[JobName.FACE_DETECTION]: QueueName.FACE_DETECTION,
[JobName.QUEUE_FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
[JobName.FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
// smart search
[JobName.QUEUE_SMART_SEARCH]: QueueName.SMART_SEARCH,
[JobName.SMART_SEARCH]: QueueName.SMART_SEARCH,
// XMP sidecars
[JobName.QUEUE_SIDECAR]: QueueName.SIDECAR,
[JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR,
[JobName.SIDECAR_SYNC]: QueueName.SIDECAR,
[JobName.SIDECAR_WRITE]: QueueName.SIDECAR,
// Library management
[JobName.LIBRARY_SCAN_ASSET]: QueueName.LIBRARY,
[JobName.LIBRARY_SCAN]: QueueName.LIBRARY,
[JobName.LIBRARY_DELETE]: QueueName.LIBRARY,
[JobName.LIBRARY_REMOVE_OFFLINE]: QueueName.LIBRARY,
[JobName.LIBRARY_QUEUE_SCAN_ALL]: QueueName.LIBRARY,
[JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY,
};

View File

@ -1,41 +0,0 @@
export interface IBaseJob {
force?: boolean;
}
export interface IEntityJob extends IBaseJob {
id: string;
source?: 'upload' | 'sidecar-write';
}
export interface IAssetDeletionJob extends IEntityJob {
fromExternal?: boolean;
}
export interface ILibraryFileJob extends IEntityJob {
ownerId: string;
assetPath: string;
}
export interface ILibraryRefreshJob extends IEntityJob {
refreshModifiedFiles: boolean;
refreshAllFiles: boolean;
}
export interface IBulkEntityJob extends IBaseJob {
ids: string[];
}
export interface IDeleteFilesJob extends IBaseJob {
files: Array<string | null | undefined>;
}
export interface ISidecarWriteJob extends IEntityJob {
description?: string;
dateTimeOriginal?: string;
latitude?: number;
longitude?: number;
}
export interface IDeferrableJob extends IEntityJob {
deferred?: boolean;
}

View File

@ -1 +0,0 @@
export const FACE_THUMBNAIL_SIZE = 250;

View File

@ -1,129 +0,0 @@
export type ModelInfo = {
dimSize: number;
};
export const CLIP_MODEL_INFO: Record<string, ModelInfo> = {
RN50__openai: {
dimSize: 1024,
},
RN50__yfcc15m: {
dimSize: 1024,
},
RN50__cc12m: {
dimSize: 1024,
},
RN101__openai: {
dimSize: 512,
},
RN101__yfcc15m: {
dimSize: 512,
},
RN50x4__openai: {
dimSize: 640,
},
RN50x16__openai: {
dimSize: 768,
},
RN50x64__openai: {
dimSize: 1024,
},
'ViT-B-32__openai': {
dimSize: 512,
},
'ViT-B-32__laion2b_e16': {
dimSize: 512,
},
'ViT-B-32__laion400m_e31': {
dimSize: 512,
},
'ViT-B-32__laion400m_e32': {
dimSize: 512,
},
'ViT-B-32__laion2b-s34b-b79k': {
dimSize: 512,
},
'ViT-B-16__openai': {
dimSize: 512,
},
'ViT-B-16__laion400m_e31': {
dimSize: 512,
},
'ViT-B-16__laion400m_e32': {
dimSize: 512,
},
'ViT-B-16-plus-240__laion400m_e31': {
dimSize: 640,
},
'ViT-B-16-plus-240__laion400m_e32': {
dimSize: 640,
},
'ViT-L-14__openai': {
dimSize: 768,
},
'ViT-L-14__laion400m_e31': {
dimSize: 768,
},
'ViT-L-14__laion400m_e32': {
dimSize: 768,
},
'ViT-L-14__laion2b-s32b-b82k': {
dimSize: 768,
},
'ViT-L-14-336__openai': {
dimSize: 768,
},
'ViT-L-14-quickgelu__dfn2b': {
dimSize: 768,
},
'ViT-H-14__laion2b-s32b-b79k': {
dimSize: 1024,
},
'ViT-H-14-quickgelu__dfn5b': {
dimSize: 1024,
},
'ViT-H-14-378-quickgelu__dfn5b': {
dimSize: 1024,
},
'ViT-g-14__laion2b-s12b-b42k': {
dimSize: 1024,
},
'LABSE-Vit-L-14': {
dimSize: 768,
},
'XLM-Roberta-Large-Vit-B-32': {
dimSize: 512,
},
'XLM-Roberta-Large-Vit-B-16Plus': {
dimSize: 640,
},
'XLM-Roberta-Large-Vit-L-14': {
dimSize: 768,
},
'XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k': {
dimSize: 1024,
},
'nllb-clip-base-siglip__v1': {
dimSize: 768,
},
'nllb-clip-large-siglip__v1': {
dimSize: 1152,
},
};
export function cleanModelName(modelName: string): string {
const token = modelName.split('/').at(-1);
if (!token) {
throw new Error(`Invalid model name: ${modelName}`);
}
return token.replaceAll(':', '_');
}
export function getCLIPModelInfo(modelName: string): ModelInfo {
const modelInfo = CLIP_MODEL_INFO[cleanModelName(modelName)];
if (!modelInfo) {
throw new Error(`Unknown CLIP model: ${modelName}`);
}
return modelInfo;
}

View File

@ -1,27 +0,0 @@
export const supportedYearTokens = ['y', 'yy'];
export const supportedMonthTokens = ['M', 'MM', 'MMM', 'MMMM'];
export const supportedWeekTokens = ['W', 'WW'];
export const supportedDayTokens = ['d', 'dd'];
export const supportedHourTokens = ['h', 'hh', 'H', 'HH'];
export const supportedMinuteTokens = ['m', 'mm'];
export const supportedSecondTokens = ['s', 'ss', 'SSS'];
export const supportedPresetTokens = [
'{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}/{{MM}}-{{dd}}/{{filename}}',
'{{y}}/{{MMMM}}-{{dd}}/{{filename}}',
'{{y}}/{{MM}}/{{filename}}',
'{{y}}/{{MMM}}/{{filename}}',
'{{y}}/{{MMMM}}/{{filename}}',
'{{y}}/{{MM}}/{{dd}}/{{filename}}',
'{{y}}/{{MMMM}}/{{dd}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}-{{MM}}-{{dd}}/{{filename}}',
'{{y}}-{{MMM}}-{{dd}}/{{filename}}',
'{{y}}-{{MMMM}}-{{dd}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}/{{filename}}',
'{{y}}/{{y}}-{{WW}}/{{filename}}',
'{{y}}/{{y}}-{{MM}}-{{dd}}/{{assetId}}',
'{{y}}/{{y}}-{{MM}}/{{assetId}}',
'{{y}}/{{y}}-{{WW}}/{{assetId}}',
'{{album}}/{{filename}}',
];

View File

@ -1,6 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty } from 'class-validator';
import { JobCommand, QueueName } from 'src/domain/job/job.constants';
import { JobCommand, QueueName } from 'src/interfaces/job.repository';
import { ValidateBoolean } from 'src/validation';
export class JobIdParamDto {

View File

@ -1,8 +1,8 @@
import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger';
import type { DateTime } from 'luxon';
import { FeatureFlags } from 'src/cores/system-config.core';
import { IVersion, VersionType } from 'src/domain/domain.constant';
import { SystemConfigThemeDto } from 'src/dtos/system-config-theme.dto';
import { IVersion, VersionType } from 'src/utils/version';
export class ServerPingResponse {
@ApiResponseProperty({ type: String, example: 'pong' })

View File

@ -1,7 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsInt, IsObject, IsPositive, ValidateNested } from 'class-validator';
import { ConcurrentQueueName, QueueName } from 'src/domain/job/job.constants';
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.repository';
export class JobSettingsDto {
@IsInt()

View File

@ -1,4 +1,4 @@
import { ConcurrentQueueName } from 'src/domain/job/job.constants';
import { ConcurrentQueueName } from 'src/interfaces/job.repository';
import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity('system_config')

View File

@ -7,7 +7,7 @@ import { CheckExistingAssetsDto } from 'src/immich/api-v1/asset/dto/check-existi
import { SearchPropertiesDto } from 'src/immich/api-v1/asset/dto/search-properties.dto';
import { CuratedLocationsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-locations-response.dto';
import { CuratedObjectsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-objects-response.dto';
import { OptionalBetween } from 'src/infra/infra.utils';
import { OptionalBetween } from 'src/utils/database';
import { In } from 'typeorm/find-options/operator/In.js';
import { Repository } from 'typeorm/repository/Repository.js';
export interface AssetCheck {

View File

@ -1,5 +1,4 @@
import { when } from 'jest-when';
import { JobName } from 'src/domain/job/job.constants';
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity';
import { ExifEntity } from 'src/entities/exif.entity';
import { IAssetRepositoryV1 } from 'src/immich/api-v1/asset/asset-repository';
@ -7,7 +6,7 @@ import { AssetService } from 'src/immich/api-v1/asset/asset.service';
import { CreateAssetDto } from 'src/immich/api-v1/asset/dto/create-asset.dto';
import { AssetRejectReason, AssetUploadAction } from 'src/immich/api-v1/asset/response-dto/asset-check-response.dto';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IJobRepository } from 'src/interfaces/job.repository';
import { IJobRepository, JobName } from 'src/interfaces/job.repository';
import { ILibraryRepository } from 'src/interfaces/library.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { IUserRepository } from 'src/interfaces/user.repository';

View File

@ -6,8 +6,6 @@ import {
NotFoundException,
} from '@nestjs/common';
import { AccessCore, Permission } from 'src/cores/access.core';
import { mimeTypes } from 'src/domain/domain.constant';
import { JobName } from 'src/domain/job/job.constants';
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity';
@ -28,15 +26,16 @@ import { AssetFileUploadResponseDto } from 'src/immich/api-v1/asset/response-dto
import { CheckExistingAssetsResponseDto } from 'src/immich/api-v1/asset/response-dto/check-existing-assets-response.dto';
import { CuratedLocationsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-locations-response.dto';
import { CuratedObjectsResponseDto } from 'src/immich/api-v1/asset/response-dto/curated-objects-response.dto';
import { ImmichLogger } from 'src/infra/logger';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IJobRepository } from 'src/interfaces/job.repository';
import { IJobRepository, JobName } from 'src/interfaces/job.repository';
import { ILibraryRepository } from 'src/interfaces/library.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { IUserRepository } from 'src/interfaces/user.repository';
import { UploadFile } from 'src/services/asset.service';
import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils';
import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils/file';
import { ImmichLogger } from 'src/utils/logger';
import { mimeTypes } from 'src/utils/mime-types';
import { QueryFailedError } from 'typeorm';
@Injectable()

View File

@ -13,12 +13,12 @@ import { writeFileSync } from 'node:fs';
import { access, constants } from 'node:fs/promises';
import path, { isAbsolute } from 'node:path';
import { promisify } from 'node:util';
import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant';
import { serverVersion } from 'src/domain/domain.constant';
import { ImmichLogger } from 'src/infra/logger';
import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME, serverVersion } from 'src/constants';
import { ImmichReadStream } from 'src/interfaces/storage.repository';
import { Metadata } from 'src/middleware/auth.guard';
import { CacheControl, ImmichFileResponse, isConnectionAborted } from 'src/utils';
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
import { ImmichLogger } from 'src/utils/logger';
import { isConnectionAborted } from 'src/utils/misc';
type SendFile = Parameters<Response['sendFile']>;
type SendFileOptions = SendFile[1];

View File

@ -1,25 +0,0 @@
import { format } from 'sql-formatter';
import { Logger } from 'typeorm';
export class SqlLogger implements Logger {
queries: string[] = [];
errors: Array<{ error: string | Error; query: string }> = [];
clear() {
this.queries = [];
this.errors = [];
}
logQuery(query: string) {
this.queries.push(format(query, { language: 'postgresql' }));
}
logQueryError(error: string | Error, query: string) {
this.errors.push({ error, query });
}
logQuerySlow() {}
logSchemaBuild() {}
logMigration() {}
log() {}
}

View File

@ -4,7 +4,7 @@ import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { ExifEntity } from 'src/entities/exif.entity';
import { ReverseGeocodeResult } from 'src/interfaces/metadata.repository';
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.repository';
import { Paginated, PaginationOptions } from 'src/utils';
import { Paginated, PaginationOptions } from 'src/utils/pagination';
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm';
export type AssetStats = Record<AssetType, number>;

View File

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

View File

@ -1,14 +1,139 @@
import { JobName, QueueName } from 'src/domain/job/job.constants';
import {
IAssetDeletionJob,
IBaseJob,
IDeferrableJob,
IDeleteFilesJob,
IEntityJob,
ILibraryFileJob,
ILibraryRefreshJob,
ISidecarWriteJob,
} from 'src/domain/job/job.interface';
export enum QueueName {
THUMBNAIL_GENERATION = 'thumbnailGeneration',
METADATA_EXTRACTION = 'metadataExtraction',
VIDEO_CONVERSION = 'videoConversion',
FACE_DETECTION = 'faceDetection',
FACIAL_RECOGNITION = 'facialRecognition',
SMART_SEARCH = 'smartSearch',
BACKGROUND_TASK = 'backgroundTask',
STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
MIGRATION = 'migration',
SEARCH = 'search',
SIDECAR = 'sidecar',
LIBRARY = 'library',
}
export type ConcurrentQueueName = Exclude<
QueueName,
QueueName.STORAGE_TEMPLATE_MIGRATION | QueueName.FACIAL_RECOGNITION
>;
export enum JobCommand {
START = 'start',
PAUSE = 'pause',
RESUME = 'resume',
EMPTY = 'empty',
CLEAR_FAILED = 'clear-failed',
}
export enum JobName {
// conversion
QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
VIDEO_CONVERSION = 'video-conversion',
// thumbnails
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail',
GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail',
GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail',
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
// metadata
QUEUE_METADATA_EXTRACTION = 'queue-metadata-extraction',
METADATA_EXTRACTION = 'metadata-extraction',
LINK_LIVE_PHOTOS = 'link-live-photos',
// user
USER_DELETION = 'user-deletion',
USER_DELETE_CHECK = 'user-delete-check',
USER_SYNC_USAGE = 'user-sync-usage',
// asset
ASSET_DELETION = 'asset-deletion',
ASSET_DELETION_CHECK = 'asset-deletion-check',
// storage template
STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration',
STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single',
// migration
QUEUE_MIGRATION = 'queue-migration',
MIGRATE_ASSET = 'migrate-asset',
MIGRATE_PERSON = 'migrate-person',
// facial recognition
PERSON_CLEANUP = 'person-cleanup',
QUEUE_FACE_DETECTION = 'queue-face-detection',
FACE_DETECTION = 'face-detection',
QUEUE_FACIAL_RECOGNITION = 'queue-facial-recognition',
FACIAL_RECOGNITION = 'facial-recognition',
// library management
LIBRARY_SCAN = 'library-refresh',
LIBRARY_SCAN_ASSET = 'library-refresh-asset',
LIBRARY_REMOVE_OFFLINE = 'library-remove-offline',
LIBRARY_DELETE = 'library-delete',
LIBRARY_QUEUE_SCAN_ALL = 'library-queue-all-refresh',
LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
// cleanup
DELETE_FILES = 'delete-files',
CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
// smart search
QUEUE_SMART_SEARCH = 'queue-smart-search',
SMART_SEARCH = 'smart-search',
// XMP sidecars
QUEUE_SIDECAR = 'queue-sidecar',
SIDECAR_DISCOVERY = 'sidecar-discovery',
SIDECAR_SYNC = 'sidecar-sync',
SIDECAR_WRITE = 'sidecar-write',
}
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
export interface IBaseJob {
force?: boolean;
}
export interface IEntityJob extends IBaseJob {
id: string;
source?: 'upload' | 'sidecar-write';
}
export interface IAssetDeletionJob extends IEntityJob {
fromExternal?: boolean;
}
export interface ILibraryFileJob extends IEntityJob {
ownerId: string;
assetPath: string;
}
export interface ILibraryRefreshJob extends IEntityJob {
refreshModifiedFiles: boolean;
refreshAllFiles: boolean;
}
export interface IBulkEntityJob extends IBaseJob {
ids: string[];
}
export interface IDeleteFilesJob extends IBaseJob {
files: Array<string | null | undefined>;
}
export interface ISidecarWriteJob extends IEntityJob {
description?: string;
dateTimeOriginal?: string;
latitude?: number;
longitude?: number;
}
export interface IDeferrableJob extends IEntityJob {
deferred?: boolean;
}
export interface JobCounts {
active: number;

View File

@ -1,7 +1,7 @@
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { Paginated, PaginationOptions } from 'src/utils';
import { Paginated, PaginationOptions } from 'src/utils/pagination';
import { FindManyOptions, FindOptionsRelations, FindOptionsSelect } from 'typeorm';
export const IPersonRepository = 'IPersonRepository';

View File

@ -2,7 +2,7 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
import { SmartInfoEntity } from 'src/entities/smart-info.entity';
import { Paginated } from 'src/utils';
import { Paginated } from 'src/utils/pagination';
export const ISearchRepository = 'ISearchRepository';

View File

@ -9,10 +9,10 @@ import {
import { Reflector } from '@nestjs/core';
import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger';
import { Request } from 'express';
import { IMMICH_API_KEY_NAME } from 'src/domain/auth/auth.constant';
import { IMMICH_API_KEY_NAME } from 'src/constants';
import { AuthDto } from 'src/dtos/auth.dto';
import { ImmichLogger } from 'src/infra/logger';
import { AuthService, LoginDetails } from 'src/services/auth.service';
import { ImmichLogger } from 'src/utils/logger';
import { UAParser } from 'ua-parser-js';
export enum Metadata {

View File

@ -8,8 +8,8 @@ import {
} from '@nestjs/common';
import { Observable, catchError, throwError } from 'rxjs';
import { routeToErrorMessage } from 'src/immich/app.utils';
import { ImmichLogger } from 'src/infra/logger';
import { isConnectionAborted } from 'src/utils';
import { ImmichLogger } from 'src/utils/logger';
import { isConnectionAborted } from 'src/utils/misc';
@Injectable()
export class ErrorInterceptor implements NestInterceptor {

View File

@ -7,9 +7,9 @@ import multer, { StorageEngine, diskStorage } from 'multer';
import { createHash, randomUUID } from 'node:crypto';
import { Observable } from 'rxjs';
import { UploadFieldName } from 'src/dtos/asset.dto';
import { ImmichLogger } from 'src/infra/logger';
import { AuthRequest } from 'src/middleware/auth.guard';
import { AssetService, UploadFile } from 'src/services/asset.service';
import { ImmichLogger } from 'src/utils/logger';
export enum Route {
ASSET = 'asset',

View File

@ -1,5 +1,5 @@
import { getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant';
import { vectorExt } from 'src/infra/database.config';
import { vectorExt } from 'src/database.config';
import { getCLIPModelInfo } from 'src/utils/misc';
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UsePgVectors1700713871511 implements MigrationInterface {

View File

@ -1,4 +1,4 @@
import { vectorExt } from 'src/infra/database.config';
import { vectorExt } from 'src/database.config';
import { DatabaseExtension } from 'src/interfaces/database.repository';
import { MigrationInterface, QueryRunner } from 'typeorm';

View File

@ -1,4 +1,4 @@
import { vectorExt } from 'src/infra/database.config';
import { vectorExt } from 'src/database.config';
import { DatabaseExtension } from 'src/interfaces/database.repository';
import { MigrationInterface, QueryRunner } from 'typeorm';

View File

@ -9,8 +9,8 @@ import { PartnerEntity } from 'src/entities/partner.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { UserTokenEntity } from 'src/entities/user-token.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Brackets, In, Repository } from 'typeorm';
type IActivityAccess = IAccessRepository['activity'];

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { ActivityEntity } from 'src/entities/activity.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IActivityRepository } from 'src/interfaces/activity.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { IsNull, Repository } from 'typeorm';
export interface ActivitySearch {

View File

@ -1,11 +1,10 @@
import { Injectable } from '@nestjs/common';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import _ from 'lodash';
import { dataSource } from 'src/database.config';
import { Chunked, ChunkedArray, DATABASE_PARAMETER_CHUNK_SIZE, DummyValue, GenerateSql } from 'src/decorators';
import { AlbumEntity } from 'src/entities/album.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { dataSource } from 'src/infra/database.config';
import { Instrumentation } from 'src/infra/instrumentation';
import {
AlbumAsset,
AlbumAssetCount,
@ -13,7 +12,8 @@ import {
AlbumInfoOptions,
IAlbumRepository,
} from 'src/interfaces/album.repository';
import { setUnion } from 'src/utils';
import { Instrumentation } from 'src/utils/instrumentation';
import { setUnion } from 'src/utils/set';
import { DataSource, FindOptionsOrder, FindOptionsRelations, In, IsNull, Not, Repository } from 'typeorm';
@Instrumentation()

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { APIKeyEntity } from 'src/entities/api-key.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IKeyRepository } from 'src/interfaces/api-key.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AssetStackEntity } from 'src/entities/asset-stack.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -8,8 +8,6 @@ import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { ExifEntity } from 'src/entities/exif.entity';
import { SmartInfoEntity } from 'src/entities/smart-info.entity';
import { OptionalBetween, paginate, paginatedBuilder, searchAssetBuilder } from 'src/infra/infra.utils';
import { Instrumentation } from 'src/infra/instrumentation';
import {
AssetBuilderOptions,
AssetCreate,
@ -32,7 +30,9 @@ import {
WithoutProperty,
} from 'src/interfaces/asset.repository';
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.repository';
import { Paginated, PaginationMode, PaginationOptions } from 'src/utils';
import { OptionalBetween, searchAssetBuilder } from 'src/utils/database';
import { Instrumentation } from 'src/utils/instrumentation';
import { Paginated, PaginationMode, PaginationOptions, paginate, paginatedBuilder } from 'src/utils/pagination';
import {
Brackets,
FindOptionsRelations,

View File

@ -1,7 +1,7 @@
import { InjectRepository } from '@nestjs/typeorm';
import { AuditEntity } from 'src/entities/audit.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { AuditSearch, IAuditRepository } from 'src/interfaces/audit.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { LessThan, MoreThan, Repository } from 'typeorm';
@Instrumentation()

View File

@ -7,8 +7,6 @@ import {
WebSocketServer,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import {
ClientEvent,
ICommunicationRepository,
@ -18,6 +16,8 @@ import {
ServerEvent,
} from 'src/interfaces/communication.repository';
import { AuthService } from 'src/services/auth.service';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
@Instrumentation()
@WebSocketGateway({

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { compareSync, hash } from 'bcrypt';
import { createHash, randomBytes, randomUUID } from 'node:crypto';
import { createReadStream } from 'node:fs';
import { Instrumentation } from 'src/infra/instrumentation';
import { ICryptoRepository } from 'src/interfaces/crypto.repository';
import { Instrumentation } from 'src/utils/instrumentation';
@Instrumentation()
@Injectable()

View File

@ -1,10 +1,7 @@
import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import AsyncLock from 'async-lock';
import { Version, VersionType } from 'src/domain/domain.constant';
import { vectorExt } from 'src/infra/database.config';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { vectorExt } from 'src/database.config';
import {
DatabaseExtension,
DatabaseLock,
@ -14,6 +11,9 @@ import {
VectorUpdateResult,
extName,
} from 'src/interfaces/database.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
import { Version, VersionType } from 'src/utils/version';
import { isValidInteger } from 'src/validation';
import { DataSource, EntityManager, QueryRunner } from 'typeorm';

View File

@ -4,10 +4,7 @@ import { glob, globStream } from 'fast-glob';
import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs';
import fs from 'node:fs/promises';
import path from 'node:path';
import { mimeTypes } from 'src/domain/domain.constant';
import { CrawlOptionsDto } from 'src/dtos/library.dto';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import {
DiskUsage,
IStorageRepository,
@ -16,6 +13,9 @@ import {
StorageEventType,
WatchEvents,
} from 'src/interfaces/storage.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
import { mimeTypes } from 'src/utils/mime-types';
@Instrumentation()
export class FilesystemProvider implements IStorageRepository {

View File

@ -6,10 +6,78 @@ import { Job, JobsOptions, Processor, Queue, Worker, WorkerOptions } from 'bullm
import { CronJob, CronTime } from 'cron';
import { setTimeout } from 'node:timers/promises';
import { bullConfig } from 'src/config';
import { JOBS_TO_QUEUE, JobName, QueueName } from 'src/domain/job/job.constants';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { IJobRepository, JobCounts, JobItem, QueueCleanType, QueueStatus } from 'src/interfaces/job.repository';
import {
IJobRepository,
JobCounts,
JobItem,
JobName,
QueueCleanType,
QueueName,
QueueStatus,
} from 'src/interfaces/job.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
// misc
[JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK,
[JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK,
[JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK,
[JobName.USER_DELETION]: QueueName.BACKGROUND_TASK,
[JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK,
[JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK,
[JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK,
[JobName.USER_SYNC_USAGE]: QueueName.BACKGROUND_TASK,
// conversion
[JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
[JobName.VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
// thumbnails
[JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
[JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
// metadata
[JobName.QUEUE_METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
[JobName.METADATA_EXTRACTION]: QueueName.METADATA_EXTRACTION,
[JobName.LINK_LIVE_PHOTOS]: QueueName.METADATA_EXTRACTION,
// storage template
[JobName.STORAGE_TEMPLATE_MIGRATION]: QueueName.STORAGE_TEMPLATE_MIGRATION,
[JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: QueueName.STORAGE_TEMPLATE_MIGRATION,
// migration
[JobName.QUEUE_MIGRATION]: QueueName.MIGRATION,
[JobName.MIGRATE_ASSET]: QueueName.MIGRATION,
[JobName.MIGRATE_PERSON]: QueueName.MIGRATION,
// facial recognition
[JobName.QUEUE_FACE_DETECTION]: QueueName.FACE_DETECTION,
[JobName.FACE_DETECTION]: QueueName.FACE_DETECTION,
[JobName.QUEUE_FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
[JobName.FACIAL_RECOGNITION]: QueueName.FACIAL_RECOGNITION,
// smart search
[JobName.QUEUE_SMART_SEARCH]: QueueName.SMART_SEARCH,
[JobName.SMART_SEARCH]: QueueName.SMART_SEARCH,
// XMP sidecars
[JobName.QUEUE_SIDECAR]: QueueName.SIDECAR,
[JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR,
[JobName.SIDECAR_SYNC]: QueueName.SIDECAR,
[JobName.SIDECAR_WRITE]: QueueName.SIDECAR,
// Library management
[JobName.LIBRARY_SCAN_ASSET]: QueueName.LIBRARY,
[JobName.LIBRARY_SCAN]: QueueName.LIBRARY,
[JobName.LIBRARY_DELETE]: QueueName.LIBRARY,
[JobName.LIBRARY_REMOVE_OFFLINE]: QueueName.LIBRARY,
[JobName.LIBRARY_QUEUE_SCAN_ALL]: QueueName.LIBRARY,
[JobName.LIBRARY_QUEUE_CLEANUP]: QueueName.LIBRARY,
};
@Instrumentation()
@Injectable()

View File

@ -3,8 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
import { LibraryEntity, LibraryType } from 'src/entities/library.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ILibraryRepository } from 'src/interfaces/library.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { IsNull, Not } from 'typeorm';
import { Repository } from 'typeorm/repository/Repository.js';

View File

@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { readFile } from 'node:fs/promises';
import { CLIPConfig, ModelConfig, RecognitionConfig } from 'src/dtos/model-config.dto';
import { Instrumentation } from 'src/infra/instrumentation';
import {
CLIPMode,
DetectFaceResult,
@ -10,6 +9,7 @@ import {
TextModelInput,
VisionModelInput,
} from 'src/interfaces/machine-learning.repository';
import { Instrumentation } from 'src/utils/instrumentation';
const errorPrefix = 'Machine learning request';

View File

@ -4,8 +4,6 @@ import { Writable } from 'node:stream';
import { promisify } from 'node:util';
import sharp from 'sharp';
import { Colorspace } from 'src/entities/system-config.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import {
CropOptions,
IMediaRepository,
@ -13,7 +11,9 @@ import {
TranscodeOptions,
VideoInfo,
} from 'src/interfaces/media.repository';
import { handlePromiseError } from 'src/utils';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
import { handlePromiseError } from 'src/utils/misc';
const probe = promisify<string, FfprobeData>(ffmpeg.ffprobe);
sharp.concurrency(0);

View File

@ -6,21 +6,15 @@ import { getName } from 'i18n-iso-countries';
import { createReadStream, existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import readLine from 'node:readline';
import { citiesFile, geodataAdmin1Path, geodataAdmin2Path, geodataCities500Path, geodataDatePath } from 'src/constants';
import { DummyValue, GenerateSql } from 'src/decorators';
import {
citiesFile,
geodataAdmin1Path,
geodataAdmin2Path,
geodataCities500Path,
geodataDatePath,
} from 'src/domain/domain.constant';
import { ExifEntity } from 'src/entities/exif.entity';
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
import { SystemMetadataKey } from 'src/entities/system-metadata.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { GeoPoint, IMetadataRepository, ImmichTags, ReverseGeocodeResult } from 'src/interfaces/metadata.repository';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
import { DataSource, QueryRunner, Repository } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { MoveEntity, PathType } from 'src/entities/move.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IMoveRepository, MoveCreate } from 'src/interfaces/move.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { PartnerEntity } from 'src/entities/partner.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { DeepPartial, Repository } from 'typeorm';
@Instrumentation()

View File

@ -4,8 +4,6 @@ import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { asVector, paginate } from 'src/infra/infra.utils';
import { Instrumentation } from 'src/infra/instrumentation';
import {
AssetFaceId,
IPersonRepository,
@ -15,7 +13,9 @@ import {
PersonStatistics,
UpdateFacesData,
} from 'src/interfaces/person.repository';
import { Paginated, PaginationOptions } from 'src/utils';
import { asVector } from 'src/utils/database';
import { Instrumentation } from 'src/utils/instrumentation';
import { Paginated, PaginationOptions, paginate } from 'src/utils/pagination';
import { FindManyOptions, FindOptionsRelations, FindOptionsSelect, In, Repository } from 'typeorm';
@Instrumentation()

View File

@ -1,16 +1,12 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { vectorExt } from 'src/database.config';
import { DummyValue, GenerateSql } from 'src/decorators';
import { getCLIPModelInfo } from 'src/domain/smart-info/smart-info.constant';
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
import { SmartInfoEntity } from 'src/entities/smart-info.entity';
import { SmartSearchEntity } from 'src/entities/smart-search.entity';
import { vectorExt } from 'src/infra/database.config';
import { asVector, paginatedBuilder, searchAssetBuilder } from 'src/infra/infra.utils';
import { Instrumentation } from 'src/infra/instrumentation';
import { ImmichLogger } from 'src/infra/logger';
import { DatabaseExtension } from 'src/interfaces/database.repository';
import {
AssetSearchOptions,
@ -21,7 +17,11 @@ import {
SearchPaginationOptions,
SmartSearchOptions,
} from 'src/interfaces/search.repository';
import { Paginated, PaginationMode, PaginationResult } from 'src/utils';
import { asVector, searchAssetBuilder } from 'src/utils/database';
import { Instrumentation } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger';
import { getCLIPModelInfo } from 'src/utils/misc';
import { Paginated, PaginationMode, PaginationResult, paginatedBuilder } from 'src/utils/pagination';
import { isValidInteger } from 'src/validation';
import { Repository, SelectQueryBuilder } from 'typeorm';

View File

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { Instrumentation } from 'src/infra/instrumentation';
import { GitHubRelease, IServerInfoRepository } from 'src/interfaces/server-info.repository';
import { Instrumentation } from 'src/utils/instrumentation';
@Instrumentation()
@Injectable()

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -2,8 +2,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { readFile } from 'node:fs/promises';
import { Chunked, DummyValue, GenerateSql } from 'src/decorators';
import { SystemConfigEntity } from 'src/entities/system-config.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { In, Repository } from 'typeorm';
@Instrumentation()

View File

@ -1,7 +1,7 @@
import { InjectRepository } from '@nestjs/typeorm';
import { SystemMetadata, SystemMetadataEntity } from 'src/entities/system-metadata.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AssetEntity } from 'src/entities/asset.entity';
import { TagEntity } from 'src/entities/tag.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { ITagRepository } from 'src/interfaces/tag.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -2,8 +2,8 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { UserTokenEntity } from 'src/entities/user-token.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import { IUserTokenRepository } from 'src/interfaces/user-token.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { Repository } from 'typeorm';
@Instrumentation()

View File

@ -3,13 +3,13 @@ import { InjectRepository } from '@nestjs/typeorm';
import { DummyValue, GenerateSql } from 'src/decorators';
import { AssetEntity } from 'src/entities/asset.entity';
import { UserEntity } from 'src/entities/user.entity';
import { Instrumentation } from 'src/infra/instrumentation';
import {
IUserRepository,
UserFindOptions,
UserListFilter,
UserStatsQueryResponse,
} from 'src/interfaces/user.repository';
import { Instrumentation } from 'src/utils/instrumentation';
import { IsNull, Not, Repository } from 'typeorm';
@Instrumentation()

View File

@ -21,7 +21,7 @@ import { IAccessRepository } from 'src/interfaces/access.repository';
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.repository';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IUserRepository } from 'src/interfaces/user.repository';
import { setUnion } from 'src/utils';
import { setUnion } from 'src/utils/set';
@Injectable()
export class AlbumService {

View File

@ -1,13 +1,12 @@
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { when } from 'jest-when';
import { JobName } from 'src/domain/job/job.constants';
import { mapAsset } from 'src/dtos/asset-response.dto';
import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/asset.dto';
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository';
import { AssetStats, IAssetRepository, TimeBucketSize } from 'src/interfaces/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository';
import { IJobRepository, JobItem } from 'src/interfaces/job.repository';
import { IJobRepository, JobItem, JobName } from 'src/interfaces/job.repository';
import { IPartnerRepository } from 'src/interfaces/partner.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';

View File

@ -6,9 +6,6 @@ import sanitize from 'sanitize-filename';
import { AccessCore, Permission } from 'src/cores/access.core';
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { mimeTypes } from 'src/domain/domain.constant';
import { JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/domain/job/job.constants';
import { IAssetDeletionJob, ISidecarWriteJob } from 'src/domain/job/job.interface';
import {
AssetResponseDto,
MemoryLaneResponseDto,
@ -31,17 +28,26 @@ import { UpdateStackParentDto } from 'src/dtos/stack.dto';
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
import { AssetEntity } from 'src/entities/asset.entity';
import { LibraryType } from 'src/entities/library.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IAssetStackRepository } from 'src/interfaces/asset-stack.repository';
import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository';
import { IJobRepository, JobItem, JobStatus } from 'src/interfaces/job.repository';
import {
IAssetDeletionJob,
IJobRepository,
ISidecarWriteJob,
JOBS_ASSET_PAGINATION_SIZE,
JobItem,
JobName,
JobStatus,
} from 'src/interfaces/job.repository';
import { IPartnerRepository } from 'src/interfaces/partner.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { IUserRepository } from 'src/interfaces/user.repository';
import { usePagination } from 'src/utils';
import { ImmichLogger } from 'src/utils/logger';
import { mimeTypes } from 'src/utils/mime-types';
import { usePagination } from 'src/utils/pagination';
export interface UploadRequest {
auth: AuthDto | null;

View File

@ -1,10 +1,9 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { DateTime } from 'luxon';
import { resolve } from 'node:path';
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
import { AccessCore, Permission } from 'src/cores/access.core';
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
import { AUDIT_LOG_MAX_DURATION } from 'src/domain/domain.constant';
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/domain/job/job.constants';
import {
AuditDeletesDto,
AuditDeletesResponseDto,
@ -16,16 +15,16 @@ import {
import { AuthDto } from 'src/dtos/auth.dto';
import { DatabaseAction } from 'src/entities/audit.entity';
import { AssetPathType, PersonPathType, UserPathType } from 'src/entities/move.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IAuditRepository } from 'src/interfaces/audit.repository';
import { ICryptoRepository } from 'src/interfaces/crypto.repository';
import { JobStatus } from 'src/interfaces/job.repository';
import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.repository';
import { IPersonRepository } from 'src/interfaces/person.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { IUserRepository } from 'src/interfaces/user.repository';
import { usePagination } from 'src/utils';
import { ImmichLogger } from 'src/utils/logger';
import { usePagination } from 'src/utils/pagination';
@Injectable()
export class AuditService {

View File

@ -2,7 +2,7 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common';
import { IncomingHttpHeaders } from 'node:http';
import { Issuer, generators } from 'openid-client';
import { Socket } from 'socket.io';
import { AuthType } from 'src/domain/auth/auth.constant';
import { AuthType } from 'src/constants';
import { AuthDto, SignUpDto } from 'src/dtos/auth.dto';
import { UserEntity } from 'src/entities/user.entity';
import { IKeyRepository } from 'src/interfaces/api-key.repository';

View File

@ -10,9 +10,6 @@ import cookieParser from 'cookie';
import { DateTime } from 'luxon';
import { IncomingHttpHeaders } from 'node:http';
import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client';
import { AccessCore, Permission } from 'src/cores/access.core';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { UserCore } from 'src/cores/user.core';
import {
AuthType,
IMMICH_ACCESS_COOKIE,
@ -21,7 +18,10 @@ import {
IMMICH_IS_AUTHENTICATED,
LOGIN_URL,
MOBILE_REDIRECT,
} from 'src/domain/auth/auth.constant';
} from 'src/constants';
import { AccessCore, Permission } from 'src/cores/access.core';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { UserCore } from 'src/cores/user.core';
import {
AuthDeviceResponseDto,
AuthDto,
@ -39,7 +39,6 @@ import {
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
import { SystemConfig } from 'src/entities/system-config.entity';
import { UserEntity } from 'src/entities/user.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IKeyRepository } from 'src/interfaces/api-key.repository';
import { ICryptoRepository } from 'src/interfaces/crypto.repository';
@ -48,7 +47,8 @@ import { ISharedLinkRepository } from 'src/interfaces/shared-link.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { IUserTokenRepository } from 'src/interfaces/user-token.repository';
import { IUserRepository } from 'src/interfaces/user.repository';
import { HumanReadableSize } from 'src/utils';
import { HumanReadableSize } from 'src/utils/bytes';
import { ImmichLogger } from 'src/utils/logger';
export interface LoginDetails {
isSecure: boolean;

View File

@ -1,7 +1,7 @@
import { Version, VersionType } from 'src/domain/domain.constant';
import { ImmichLogger } from 'src/infra/logger';
import { DatabaseExtension, IDatabaseRepository, VectorIndex } from 'src/interfaces/database.repository';
import { DatabaseService } from 'src/services/database.service';
import { ImmichLogger } from 'src/utils/logger';
import { Version, VersionType } from 'src/utils/version';
import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock';
describe(DatabaseService.name, () => {

View File

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

View File

@ -4,7 +4,7 @@ import { DownloadResponseDto } from 'src/dtos/download.dto';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IStorageRepository } from 'src/interfaces/storage.repository';
import { DownloadService } from 'src/services/download.service';
import { CacheControl, ImmichFileResponse } from 'src/utils';
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
import { assetStub } from 'test/fixtures/asset.stub';
import { authStub } from 'test/fixtures/auth.stub';
import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock';

View File

@ -1,7 +1,6 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { parse } from 'node:path';
import { AccessCore, Permission } from 'src/cores/access.core';
import { mimeTypes } from 'src/domain/domain.constant';
import { AssetIdsDto } from 'src/dtos/asset.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto';
@ -9,7 +8,10 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { IAccessRepository } from 'src/interfaces/access.repository';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { IStorageRepository, ImmichReadStream } from 'src/interfaces/storage.repository';
import { CacheControl, HumanReadableSize, ImmichFileResponse, usePagination } from 'src/utils';
import { HumanReadableSize } from 'src/utils/bytes';
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
import { mimeTypes } from 'src/utils/mime-types';
import { usePagination } from 'src/utils/pagination';
@Injectable()
export class DownloadService {

View File

@ -1,10 +1,17 @@
import { BadRequestException } from '@nestjs/common';
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
import { JobCommand, JobName, QueueName } from 'src/domain/job/job.constants';
import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { ICommunicationRepository } from 'src/interfaces/communication.repository';
import { IJobRepository, JobHandler, JobItem, JobStatus } from 'src/interfaces/job.repository';
import {
IJobRepository,
JobCommand,
JobHandler,
JobItem,
JobName,
JobStatus,
QueueName,
} from 'src/interfaces/job.repository';
import { IPersonRepository } from 'src/interfaces/person.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { JobService } from 'src/services/job.service';

View File

@ -1,15 +1,24 @@
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { FeatureFlag, SystemConfigCore } from 'src/cores/system-config.core';
import { ConcurrentQueueName, JobCommand, JobName, QueueName } from 'src/domain/job/job.constants';
import { mapAsset } from 'src/dtos/asset-response.dto';
import { AllJobStatusResponseDto, JobCommandDto, JobStatusDto } from 'src/dtos/job.dto';
import { AssetType } from 'src/entities/asset.entity';
import { ImmichLogger } from 'src/infra/logger';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { ClientEvent, ICommunicationRepository } from 'src/interfaces/communication.repository';
import { IJobRepository, JobHandler, JobItem, JobStatus, QueueCleanType } from 'src/interfaces/job.repository';
import {
ConcurrentQueueName,
IJobRepository,
JobCommand,
JobHandler,
JobItem,
JobName,
JobStatus,
QueueCleanType,
QueueName,
} from 'src/interfaces/job.repository';
import { IPersonRepository } from 'src/interfaces/person.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';
import { ImmichLogger } from 'src/utils/logger';
@Injectable()
export class JobService {

View File

@ -3,8 +3,6 @@ import { when } from 'jest-when';
import { R_OK } from 'node:constants';
import { Stats } from 'node:fs';
import { SystemConfigCore } from 'src/cores/system-config.core';
import { JobName } from 'src/domain/job/job.constants';
import { ILibraryFileJob, ILibraryRefreshJob } from 'src/domain/job/job.interface';
import { mapLibrary } from 'src/dtos/library.dto';
import { AssetType } from 'src/entities/asset.entity';
import { LibraryType } from 'src/entities/library.entity';
@ -13,7 +11,7 @@ import { UserEntity } from 'src/entities/user.entity';
import { IAssetRepository } from 'src/interfaces/asset.repository';
import { ICryptoRepository } from 'src/interfaces/crypto.repository';
import { IDatabaseRepository } from 'src/interfaces/database.repository';
import { IJobRepository, JobStatus } from 'src/interfaces/job.repository';
import { IJobRepository, ILibraryFileJob, ILibraryRefreshJob, JobName, JobStatus } from 'src/interfaces/job.repository';
import { ILibraryRepository } from 'src/interfaces/library.repository';
import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.repository';
import { ISystemConfigRepository } from 'src/interfaces/system-config.repository';

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