mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	refactor: last repository (#16042)
This commit is contained in:
		
							parent
							
								
									5f3a42a132
								
							
						
					
					
						commit
						fa5aeaf539
					
				@ -13,15 +13,15 @@ import { IWorker } from 'src/constants';
 | 
			
		||||
import { controllers } from 'src/controllers';
 | 
			
		||||
import { entities } from 'src/entities';
 | 
			
		||||
import { ImmichWorker } from 'src/enum';
 | 
			
		||||
import { IJobRepository } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AuthGuard } from 'src/middleware/auth.guard';
 | 
			
		||||
import { ErrorInterceptor } from 'src/middleware/error.interceptor';
 | 
			
		||||
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
 | 
			
		||||
import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
 | 
			
		||||
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
 | 
			
		||||
import { providers, repositories } from 'src/repositories';
 | 
			
		||||
import { repositories } from 'src/repositories';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { EventRepository } from 'src/repositories/event.repository';
 | 
			
		||||
import { JobRepository } from 'src/repositories/job.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository';
 | 
			
		||||
import { services } from 'src/services';
 | 
			
		||||
@ -29,7 +29,7 @@ import { AuthService } from 'src/services/auth.service';
 | 
			
		||||
import { CliService } from 'src/services/cli.service';
 | 
			
		||||
import { DatabaseService } from 'src/services/database.service';
 | 
			
		||||
 | 
			
		||||
const common = [...services, ...providers, ...repositories];
 | 
			
		||||
const common = [...repositories, ...services];
 | 
			
		||||
 | 
			
		||||
const middleware = [
 | 
			
		||||
  FileUploadInterceptor,
 | 
			
		||||
@ -80,7 +80,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
 | 
			
		||||
    @Inject(IWorker) private worker: ImmichWorker,
 | 
			
		||||
    logger: LoggingRepository,
 | 
			
		||||
    private eventRepository: EventRepository,
 | 
			
		||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
			
		||||
    private jobRepository: JobRepository,
 | 
			
		||||
    private telemetryRepository: TelemetryRepository,
 | 
			
		||||
    private authService: AuthService,
 | 
			
		||||
  ) {
 | 
			
		||||
@ -88,7 +88,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async onModuleInit() {
 | 
			
		||||
    this.telemetryRepository.setup({ repositories: [...providers.map(({ useClass }) => useClass), ...repositories] });
 | 
			
		||||
    this.telemetryRepository.setup({ repositories });
 | 
			
		||||
 | 
			
		||||
    this.jobRepository.setup({ services });
 | 
			
		||||
    if (this.worker === ImmichWorker.MICROSERVICES) {
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import { Reflector } from '@nestjs/core';
 | 
			
		||||
import { SchedulerRegistry } from '@nestjs/schedule';
 | 
			
		||||
import { Test } from '@nestjs/testing';
 | 
			
		||||
import { TypeOrmModule } from '@nestjs/typeorm';
 | 
			
		||||
import { ClassConstructor } from 'class-transformer';
 | 
			
		||||
import { PostgresJSDialect } from 'kysely-postgres-js';
 | 
			
		||||
import { KyselyModule } from 'nestjs-kysely';
 | 
			
		||||
import { OpenTelemetryModule } from 'nestjs-otel';
 | 
			
		||||
@ -13,7 +14,7 @@ import postgres from 'postgres';
 | 
			
		||||
import { format } from 'sql-formatter';
 | 
			
		||||
import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators';
 | 
			
		||||
import { entities } from 'src/entities';
 | 
			
		||||
import { providers, repositories } from 'src/repositories';
 | 
			
		||||
import { repositories } from 'src/repositories';
 | 
			
		||||
import { AccessRepository } from 'src/repositories/access.repository';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
@ -45,8 +46,7 @@ export class SqlLogger implements Logger {
 | 
			
		||||
 | 
			
		||||
const reflector = new Reflector();
 | 
			
		||||
 | 
			
		||||
type Repository = (typeof providers)[0]['useClass'];
 | 
			
		||||
type Provider = { provide: any; useClass: Repository };
 | 
			
		||||
type Repository = ClassConstructor<any>;
 | 
			
		||||
type SqlGeneratorOptions = { targetDir: string };
 | 
			
		||||
 | 
			
		||||
class SqlGenerator {
 | 
			
		||||
@ -59,15 +59,11 @@ class SqlGenerator {
 | 
			
		||||
  async run() {
 | 
			
		||||
    try {
 | 
			
		||||
      await this.setup();
 | 
			
		||||
      const targets = [
 | 
			
		||||
        ...providers,
 | 
			
		||||
        ...repositories.map((repository) => ({ provide: repository, useClass: repository as any })),
 | 
			
		||||
      ];
 | 
			
		||||
      for (const repository of targets) {
 | 
			
		||||
        if (repository.provide === LoggingRepository) {
 | 
			
		||||
      for (const Repository of repositories) {
 | 
			
		||||
        if (Repository === LoggingRepository) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        await this.process(repository);
 | 
			
		||||
        await this.process(Repository);
 | 
			
		||||
      }
 | 
			
		||||
      await this.write();
 | 
			
		||||
      this.stats();
 | 
			
		||||
@ -105,19 +101,19 @@ class SqlGenerator {
 | 
			
		||||
        TypeOrmModule.forFeature(entities),
 | 
			
		||||
        OpenTelemetryModule.forRoot(otel),
 | 
			
		||||
      ],
 | 
			
		||||
      providers: [...providers, ...repositories, AuthService, SchedulerRegistry],
 | 
			
		||||
      providers: [...repositories, AuthService, SchedulerRegistry],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    this.app = await moduleFixture.createNestApplication().init();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async process({ provide: token, useClass: Repository }: Provider) {
 | 
			
		||||
  async process(Repository: Repository) {
 | 
			
		||||
    if (!this.app) {
 | 
			
		||||
      throw new Error('Not initialized');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const data: string[] = [`-- NOTE: This file is auto generated by ./sql-generator`];
 | 
			
		||||
    const instance = this.app.get<Repository>(token);
 | 
			
		||||
    const instance = this.app.get<Repository>(Repository);
 | 
			
		||||
 | 
			
		||||
    // normal repositories
 | 
			
		||||
    data.push(...(await this.runTargets(instance, `${Repository.name}`)));
 | 
			
		||||
 | 
			
		||||
@ -5,14 +5,14 @@ import {
 | 
			
		||||
  CQMode,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  LogLevel,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  ToneMapping,
 | 
			
		||||
  TranscodeHWAccel,
 | 
			
		||||
  TranscodePolicy,
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
  VideoContainer,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImageOptions } from 'src/types';
 | 
			
		||||
import { ConcurrentQueueName, ImageOptions } from 'src/types';
 | 
			
		||||
 | 
			
		||||
export interface SystemConfig {
 | 
			
		||||
  backup: {
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { Duration } from 'luxon';
 | 
			
		||||
import { readFileSync } from 'node:fs';
 | 
			
		||||
import { SemVer } from 'semver';
 | 
			
		||||
import { ExifOrientation } from 'src/enum';
 | 
			
		||||
import { DatabaseExtension, ExifOrientation } from 'src/enum';
 | 
			
		||||
 | 
			
		||||
export const POSTGRES_VERSION_RANGE = '>=14.0.0';
 | 
			
		||||
export const VECTORS_VERSION_RANGE = '>=0.2 <0.4';
 | 
			
		||||
@ -16,6 +16,16 @@ export const LIFECYCLE_EXTENSION = 'x-immich-lifecycle';
 | 
			
		||||
export const DEPRECATED_IN_PREFIX = 'This property was deprecated in ';
 | 
			
		||||
export const ADDED_IN_PREFIX = 'This property was added in ';
 | 
			
		||||
 | 
			
		||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
 | 
			
		||||
export const JOBS_LIBRARY_PAGINATION_SIZE = 10_000;
 | 
			
		||||
 | 
			
		||||
export const EXTENSION_NAMES: Record<DatabaseExtension, string> = {
 | 
			
		||||
  cube: 'cube',
 | 
			
		||||
  earthdistance: 'earthdistance',
 | 
			
		||||
  vector: 'pgvector',
 | 
			
		||||
  vectors: 'pgvecto.rs',
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export const SALT_ROUNDS = 10;
 | 
			
		||||
 | 
			
		||||
export const IWorker = 'IWorker';
 | 
			
		||||
 | 
			
		||||
@ -35,9 +35,10 @@ import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { ImmichHeader, RouteKey } from 'src/enum';
 | 
			
		||||
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
 | 
			
		||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
 | 
			
		||||
import { FileUploadInterceptor, UploadFiles, getFiles } from 'src/middleware/file-upload.interceptor';
 | 
			
		||||
import { FileUploadInterceptor, getFiles } from 'src/middleware/file-upload.interceptor';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { AssetMediaService } from 'src/services/asset-media.service';
 | 
			
		||||
import { UploadFiles } from 'src/types';
 | 
			
		||||
import { sendFile } from 'src/utils/file';
 | 
			
		||||
import { FileNotEmptyValidator, UUIDParamDto } from 'src/validation';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,7 @@ import { SetMetadata, applyDecorators } from '@nestjs/common';
 | 
			
		||||
import { ApiExtension, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants';
 | 
			
		||||
import { ImmichWorker, MetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichWorker, JobName, MetadataKey, QueueName } from 'src/enum';
 | 
			
		||||
import { EmitEvent } from 'src/repositories/event.repository';
 | 
			
		||||
import { setUnion } from 'src/utils/set';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import { ApiProperty } from '@nestjs/swagger';
 | 
			
		||||
import { IsEnum, IsNotEmpty } from 'class-validator';
 | 
			
		||||
import { ManualJobName } from 'src/enum';
 | 
			
		||||
import { JobCommand, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobCommand, ManualJobName, QueueName } from 'src/enum';
 | 
			
		||||
import { ValidateBoolean } from 'src/validation';
 | 
			
		||||
 | 
			
		||||
export class JobIdParamDto {
 | 
			
		||||
 | 
			
		||||
@ -25,13 +25,14 @@ import {
 | 
			
		||||
  Colorspace,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  LogLevel,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  ToneMapping,
 | 
			
		||||
  TranscodeHWAccel,
 | 
			
		||||
  TranscodePolicy,
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
  VideoContainer,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { ConcurrentQueueName, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ConcurrentQueueName } from 'src/types';
 | 
			
		||||
import { IsCronExpression, ValidateBoolean } from 'src/validation';
 | 
			
		||||
 | 
			
		||||
const isLibraryScanEnabled = (config: SystemConfigLibraryScanDto) => config.enabled;
 | 
			
		||||
 | 
			
		||||
@ -398,3 +398,142 @@ export enum BootstrapEventPriority {
 | 
			
		||||
  // Initialise config after other bootstrap services, stop other services from using config on bootstrap
 | 
			
		||||
  SystemConfig = 100,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum QueueName {
 | 
			
		||||
  THUMBNAIL_GENERATION = 'thumbnailGeneration',
 | 
			
		||||
  METADATA_EXTRACTION = 'metadataExtraction',
 | 
			
		||||
  VIDEO_CONVERSION = 'videoConversion',
 | 
			
		||||
  FACE_DETECTION = 'faceDetection',
 | 
			
		||||
  FACIAL_RECOGNITION = 'facialRecognition',
 | 
			
		||||
  SMART_SEARCH = 'smartSearch',
 | 
			
		||||
  DUPLICATE_DETECTION = 'duplicateDetection',
 | 
			
		||||
  BACKGROUND_TASK = 'backgroundTask',
 | 
			
		||||
  STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
 | 
			
		||||
  MIGRATION = 'migration',
 | 
			
		||||
  SEARCH = 'search',
 | 
			
		||||
  SIDECAR = 'sidecar',
 | 
			
		||||
  LIBRARY = 'library',
 | 
			
		||||
  NOTIFICATION = 'notifications',
 | 
			
		||||
  BACKUP_DATABASE = 'backupDatabase',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum JobName {
 | 
			
		||||
  //backups
 | 
			
		||||
  BACKUP_DATABASE = 'database-backup',
 | 
			
		||||
 | 
			
		||||
  // conversion
 | 
			
		||||
  QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
 | 
			
		||||
  VIDEO_CONVERSION = 'video-conversion',
 | 
			
		||||
 | 
			
		||||
  // thumbnails
 | 
			
		||||
  QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
 | 
			
		||||
  GENERATE_THUMBNAILS = 'generate-thumbnails',
 | 
			
		||||
  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',
 | 
			
		||||
 | 
			
		||||
  // tags
 | 
			
		||||
  TAG_CLEANUP = 'tag-cleanup',
 | 
			
		||||
 | 
			
		||||
  // 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_QUEUE_SYNC_FILES = 'library-queue-sync-files',
 | 
			
		||||
  LIBRARY_QUEUE_SYNC_ASSETS = 'library-queue-sync-assets',
 | 
			
		||||
  LIBRARY_SYNC_FILE = 'library-sync-file',
 | 
			
		||||
  LIBRARY_SYNC_ASSET = 'library-sync-asset',
 | 
			
		||||
  LIBRARY_DELETE = 'library-delete',
 | 
			
		||||
  LIBRARY_QUEUE_SYNC_ALL = 'library-queue-sync-all',
 | 
			
		||||
  LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
 | 
			
		||||
 | 
			
		||||
  // cleanup
 | 
			
		||||
  DELETE_FILES = 'delete-files',
 | 
			
		||||
  CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
 | 
			
		||||
  CLEAN_OLD_SESSION_TOKENS = 'clean-old-session-tokens',
 | 
			
		||||
 | 
			
		||||
  // smart search
 | 
			
		||||
  QUEUE_SMART_SEARCH = 'queue-smart-search',
 | 
			
		||||
  SMART_SEARCH = 'smart-search',
 | 
			
		||||
 | 
			
		||||
  QUEUE_TRASH_EMPTY = 'queue-trash-empty',
 | 
			
		||||
 | 
			
		||||
  // duplicate detection
 | 
			
		||||
  QUEUE_DUPLICATE_DETECTION = 'queue-duplicate-detection',
 | 
			
		||||
  DUPLICATE_DETECTION = 'duplicate-detection',
 | 
			
		||||
 | 
			
		||||
  // XMP sidecars
 | 
			
		||||
  QUEUE_SIDECAR = 'queue-sidecar',
 | 
			
		||||
  SIDECAR_DISCOVERY = 'sidecar-discovery',
 | 
			
		||||
  SIDECAR_SYNC = 'sidecar-sync',
 | 
			
		||||
  SIDECAR_WRITE = 'sidecar-write',
 | 
			
		||||
 | 
			
		||||
  // Notification
 | 
			
		||||
  NOTIFY_SIGNUP = 'notify-signup',
 | 
			
		||||
  NOTIFY_ALBUM_INVITE = 'notify-album-invite',
 | 
			
		||||
  NOTIFY_ALBUM_UPDATE = 'notify-album-update',
 | 
			
		||||
  SEND_EMAIL = 'notification-send-email',
 | 
			
		||||
 | 
			
		||||
  // Version check
 | 
			
		||||
  VERSION_CHECK = 'version-check',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum JobCommand {
 | 
			
		||||
  START = 'start',
 | 
			
		||||
  PAUSE = 'pause',
 | 
			
		||||
  RESUME = 'resume',
 | 
			
		||||
  EMPTY = 'empty',
 | 
			
		||||
  CLEAR_FAILED = 'clear-failed',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum JobStatus {
 | 
			
		||||
  SUCCESS = 'success',
 | 
			
		||||
  FAILED = 'failed',
 | 
			
		||||
  SKIPPED = 'skipped',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum QueueCleanType {
 | 
			
		||||
  FAILED = 'failed',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum VectorIndex {
 | 
			
		||||
  CLIP = 'clip_index',
 | 
			
		||||
  FACE = 'face_index',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum DatabaseLock {
 | 
			
		||||
  GeodataImport = 100,
 | 
			
		||||
  Migrations = 200,
 | 
			
		||||
  SystemFileMounts = 300,
 | 
			
		||||
  StorageTemplateMigration = 420,
 | 
			
		||||
  VersionHistory = 500,
 | 
			
		||||
  CLIPDimSize = 512,
 | 
			
		||||
  Library = 1337,
 | 
			
		||||
  GetSystemConfig = 69,
 | 
			
		||||
  BackupDatabase = 42,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,329 +0,0 @@
 | 
			
		||||
import { ClassConstructor } from 'class-transformer';
 | 
			
		||||
import { EmailImageAttachment } from 'src/repositories/notification.repository';
 | 
			
		||||
 | 
			
		||||
export enum QueueName {
 | 
			
		||||
  THUMBNAIL_GENERATION = 'thumbnailGeneration',
 | 
			
		||||
  METADATA_EXTRACTION = 'metadataExtraction',
 | 
			
		||||
  VIDEO_CONVERSION = 'videoConversion',
 | 
			
		||||
  FACE_DETECTION = 'faceDetection',
 | 
			
		||||
  FACIAL_RECOGNITION = 'facialRecognition',
 | 
			
		||||
  SMART_SEARCH = 'smartSearch',
 | 
			
		||||
  DUPLICATE_DETECTION = 'duplicateDetection',
 | 
			
		||||
  BACKGROUND_TASK = 'backgroundTask',
 | 
			
		||||
  STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
 | 
			
		||||
  MIGRATION = 'migration',
 | 
			
		||||
  SEARCH = 'search',
 | 
			
		||||
  SIDECAR = 'sidecar',
 | 
			
		||||
  LIBRARY = 'library',
 | 
			
		||||
  NOTIFICATION = 'notifications',
 | 
			
		||||
  BACKUP_DATABASE = 'backupDatabase',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type ConcurrentQueueName = Exclude<
 | 
			
		||||
  QueueName,
 | 
			
		||||
  | QueueName.STORAGE_TEMPLATE_MIGRATION
 | 
			
		||||
  | QueueName.FACIAL_RECOGNITION
 | 
			
		||||
  | QueueName.DUPLICATE_DETECTION
 | 
			
		||||
  | QueueName.BACKUP_DATABASE
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
export enum JobCommand {
 | 
			
		||||
  START = 'start',
 | 
			
		||||
  PAUSE = 'pause',
 | 
			
		||||
  RESUME = 'resume',
 | 
			
		||||
  EMPTY = 'empty',
 | 
			
		||||
  CLEAR_FAILED = 'clear-failed',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum JobName {
 | 
			
		||||
  //backups
 | 
			
		||||
  BACKUP_DATABASE = 'database-backup',
 | 
			
		||||
 | 
			
		||||
  // conversion
 | 
			
		||||
  QUEUE_VIDEO_CONVERSION = 'queue-video-conversion',
 | 
			
		||||
  VIDEO_CONVERSION = 'video-conversion',
 | 
			
		||||
 | 
			
		||||
  // thumbnails
 | 
			
		||||
  QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
 | 
			
		||||
  GENERATE_THUMBNAILS = 'generate-thumbnails',
 | 
			
		||||
  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',
 | 
			
		||||
 | 
			
		||||
  // tags
 | 
			
		||||
  TAG_CLEANUP = 'tag-cleanup',
 | 
			
		||||
 | 
			
		||||
  // 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_QUEUE_SYNC_FILES = 'library-queue-sync-files',
 | 
			
		||||
  LIBRARY_QUEUE_SYNC_ASSETS = 'library-queue-sync-assets',
 | 
			
		||||
  LIBRARY_SYNC_FILE = 'library-sync-file',
 | 
			
		||||
  LIBRARY_SYNC_ASSET = 'library-sync-asset',
 | 
			
		||||
  LIBRARY_DELETE = 'library-delete',
 | 
			
		||||
  LIBRARY_QUEUE_SYNC_ALL = 'library-queue-sync-all',
 | 
			
		||||
  LIBRARY_QUEUE_CLEANUP = 'library-queue-cleanup',
 | 
			
		||||
 | 
			
		||||
  // cleanup
 | 
			
		||||
  DELETE_FILES = 'delete-files',
 | 
			
		||||
  CLEAN_OLD_AUDIT_LOGS = 'clean-old-audit-logs',
 | 
			
		||||
  CLEAN_OLD_SESSION_TOKENS = 'clean-old-session-tokens',
 | 
			
		||||
 | 
			
		||||
  // smart search
 | 
			
		||||
  QUEUE_SMART_SEARCH = 'queue-smart-search',
 | 
			
		||||
  SMART_SEARCH = 'smart-search',
 | 
			
		||||
 | 
			
		||||
  QUEUE_TRASH_EMPTY = 'queue-trash-empty',
 | 
			
		||||
 | 
			
		||||
  // duplicate detection
 | 
			
		||||
  QUEUE_DUPLICATE_DETECTION = 'queue-duplicate-detection',
 | 
			
		||||
  DUPLICATE_DETECTION = 'duplicate-detection',
 | 
			
		||||
 | 
			
		||||
  // XMP sidecars
 | 
			
		||||
  QUEUE_SIDECAR = 'queue-sidecar',
 | 
			
		||||
  SIDECAR_DISCOVERY = 'sidecar-discovery',
 | 
			
		||||
  SIDECAR_SYNC = 'sidecar-sync',
 | 
			
		||||
  SIDECAR_WRITE = 'sidecar-write',
 | 
			
		||||
 | 
			
		||||
  // Notification
 | 
			
		||||
  NOTIFY_SIGNUP = 'notify-signup',
 | 
			
		||||
  NOTIFY_ALBUM_INVITE = 'notify-album-invite',
 | 
			
		||||
  NOTIFY_ALBUM_UPDATE = 'notify-album-update',
 | 
			
		||||
  SEND_EMAIL = 'notification-send-email',
 | 
			
		||||
 | 
			
		||||
  // Version check
 | 
			
		||||
  VERSION_CHECK = 'version-check',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
 | 
			
		||||
export const JOBS_LIBRARY_PAGINATION_SIZE = 10_000;
 | 
			
		||||
 | 
			
		||||
export interface IBaseJob {
 | 
			
		||||
  force?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDelayedJob extends IBaseJob {
 | 
			
		||||
  /** The minimum time to wait to execute this job, in milliseconds. */
 | 
			
		||||
  delay?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEntityJob extends IBaseJob {
 | 
			
		||||
  id: string;
 | 
			
		||||
  source?: 'upload' | 'sidecar-write' | 'copy';
 | 
			
		||||
  notify?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IAssetDeleteJob extends IEntityJob {
 | 
			
		||||
  deleteOnDisk: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILibraryFileJob extends IEntityJob {
 | 
			
		||||
  ownerId: string;
 | 
			
		||||
  assetPath: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILibraryAssetJob extends IEntityJob {
 | 
			
		||||
  importPaths: string[];
 | 
			
		||||
  exclusionPatterns: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
  rating?: number;
 | 
			
		||||
  tags?: true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDeferrableJob extends IEntityJob {
 | 
			
		||||
  deferred?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INightlyJob extends IBaseJob {
 | 
			
		||||
  nightly?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEmailJob {
 | 
			
		||||
  to: string;
 | 
			
		||||
  subject: string;
 | 
			
		||||
  html: string;
 | 
			
		||||
  text: string;
 | 
			
		||||
  imageAttachments?: EmailImageAttachment[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifySignupJob extends IEntityJob {
 | 
			
		||||
  tempPassword?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifyAlbumInviteJob extends IEntityJob {
 | 
			
		||||
  recipientId: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifyAlbumUpdateJob extends IEntityJob, IDelayedJob {
 | 
			
		||||
  recipientIds: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface JobCounts {
 | 
			
		||||
  active: number;
 | 
			
		||||
  completed: number;
 | 
			
		||||
  failed: number;
 | 
			
		||||
  delayed: number;
 | 
			
		||||
  waiting: number;
 | 
			
		||||
  paused: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface QueueStatus {
 | 
			
		||||
  isActive: boolean;
 | 
			
		||||
  isPaused: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum QueueCleanType {
 | 
			
		||||
  FAILED = 'failed',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type JobItem =
 | 
			
		||||
  // Backups
 | 
			
		||||
  | { name: JobName.BACKUP_DATABASE; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Transcoding
 | 
			
		||||
  | { name: JobName.QUEUE_VIDEO_CONVERSION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.VIDEO_CONVERSION; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Thumbnails
 | 
			
		||||
  | { name: JobName.QUEUE_GENERATE_THUMBNAILS; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.GENERATE_THUMBNAILS; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // User
 | 
			
		||||
  | { name: JobName.USER_DELETE_CHECK; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.USER_DELETION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.USER_SYNC_USAGE; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Storage Template
 | 
			
		||||
  | { name: JobName.STORAGE_TEMPLATE_MIGRATION; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Migration
 | 
			
		||||
  | { name: JobName.QUEUE_MIGRATION; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.MIGRATE_ASSET; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.MIGRATE_PERSON; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Metadata Extraction
 | 
			
		||||
  | { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.METADATA_EXTRACTION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LINK_LIVE_PHOTOS; data: IEntityJob }
 | 
			
		||||
  // Sidecar Scanning
 | 
			
		||||
  | { name: JobName.QUEUE_SIDECAR; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_DISCOVERY; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_SYNC; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_WRITE; data: ISidecarWriteJob }
 | 
			
		||||
 | 
			
		||||
  // Facial Recognition
 | 
			
		||||
  | { name: JobName.QUEUE_FACE_DETECTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.FACE_DETECTION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.QUEUE_FACIAL_RECOGNITION; data: INightlyJob }
 | 
			
		||||
  | { name: JobName.FACIAL_RECOGNITION; data: IDeferrableJob }
 | 
			
		||||
  | { name: JobName.GENERATE_PERSON_THUMBNAIL; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Smart Search
 | 
			
		||||
  | { name: JobName.QUEUE_SMART_SEARCH; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.SMART_SEARCH; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.QUEUE_TRASH_EMPTY; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Duplicate Detection
 | 
			
		||||
  | { name: JobName.QUEUE_DUPLICATE_DETECTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.DUPLICATE_DETECTION; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Filesystem
 | 
			
		||||
  | { name: JobName.DELETE_FILES; data: IDeleteFilesJob }
 | 
			
		||||
 | 
			
		||||
  // Cleanup
 | 
			
		||||
  | { name: JobName.CLEAN_OLD_AUDIT_LOGS; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.CLEAN_OLD_SESSION_TOKENS; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Tags
 | 
			
		||||
  | { name: JobName.TAG_CLEANUP; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Asset Deletion
 | 
			
		||||
  | { name: JobName.PERSON_CLEANUP; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.ASSET_DELETION; data: IAssetDeleteJob }
 | 
			
		||||
  | { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Library Management
 | 
			
		||||
  | { name: JobName.LIBRARY_SYNC_FILE; data: ILibraryFileJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_FILES; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_ASSETS; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_SYNC_ASSET; data: ILibraryAssetJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_DELETE; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_ALL; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Notification
 | 
			
		||||
  | { name: JobName.SEND_EMAIL; data: IEmailJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_ALBUM_INVITE; data: INotifyAlbumInviteJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_ALBUM_UPDATE; data: INotifyAlbumUpdateJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_SIGNUP; data: INotifySignupJob }
 | 
			
		||||
 | 
			
		||||
  // Version check
 | 
			
		||||
  | { name: JobName.VERSION_CHECK; data: IBaseJob };
 | 
			
		||||
 | 
			
		||||
export enum JobStatus {
 | 
			
		||||
  SUCCESS = 'success',
 | 
			
		||||
  FAILED = 'failed',
 | 
			
		||||
  SKIPPED = 'skipped',
 | 
			
		||||
}
 | 
			
		||||
export type Jobs = { [K in JobItem['name']]: (JobItem & { name: K })['data'] };
 | 
			
		||||
export type JobOf<T extends JobName> = Jobs[T];
 | 
			
		||||
 | 
			
		||||
export const IJobRepository = 'IJobRepository';
 | 
			
		||||
 | 
			
		||||
export interface IJobRepository {
 | 
			
		||||
  setup(options: { services: ClassConstructor<unknown>[] }): void;
 | 
			
		||||
  startWorkers(): void;
 | 
			
		||||
  run(job: JobItem): Promise<JobStatus>;
 | 
			
		||||
  setConcurrency(queueName: QueueName, concurrency: number): void;
 | 
			
		||||
  queue(item: JobItem): Promise<void>;
 | 
			
		||||
  queueAll(items: JobItem[]): Promise<void>;
 | 
			
		||||
  pause(name: QueueName): Promise<void>;
 | 
			
		||||
  resume(name: QueueName): Promise<void>;
 | 
			
		||||
  empty(name: QueueName): Promise<void>;
 | 
			
		||||
  clear(name: QueueName, type: QueueCleanType): Promise<string[]>;
 | 
			
		||||
  getQueueStatus(name: QueueName): Promise<QueueStatus>;
 | 
			
		||||
  getJobCounts(name: QueueName): Promise<JobCounts>;
 | 
			
		||||
  waitForQueueCompletion(...queues: QueueName[]): Promise<void>;
 | 
			
		||||
  removeJob(jobId: string, name: JobName): Promise<IEntityJob | undefined>;
 | 
			
		||||
}
 | 
			
		||||
@ -10,14 +10,10 @@ import { UploadFieldName } from 'src/dtos/asset-media.dto';
 | 
			
		||||
import { RouteKey } from 'src/enum';
 | 
			
		||||
import { AuthRequest } from 'src/middleware/auth.guard';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { AssetMediaService, UploadFile } from 'src/services/asset-media.service';
 | 
			
		||||
import { AssetMediaService } from 'src/services/asset-media.service';
 | 
			
		||||
import { ImmichFile, UploadFile, UploadFiles } from 'src/types';
 | 
			
		||||
import { asRequest, mapToUploadFile } from 'src/utils/asset.util';
 | 
			
		||||
 | 
			
		||||
export interface UploadFiles {
 | 
			
		||||
  assetData: ImmichFile[];
 | 
			
		||||
  sidecarData: ImmichFile[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getFile(files: UploadFiles, property: 'assetData' | 'sidecarData') {
 | 
			
		||||
  const file = files[property]?.[0];
 | 
			
		||||
  return file ? mapToUploadFile(file) : file;
 | 
			
		||||
@ -30,12 +26,6 @@ export function getFiles(files: UploadFiles) {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ImmichFile extends Express.Multer.File {
 | 
			
		||||
  /** sha1 hash of file */
 | 
			
		||||
  uuid: string;
 | 
			
		||||
  checksum: Buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DiskStorageCallback = (error: Error | null, result: string) => void;
 | 
			
		||||
 | 
			
		||||
type ImmichMulterFile = Express.Multer.File & { uuid: string };
 | 
			
		||||
 | 
			
		||||
@ -13,9 +13,16 @@ import { Notice } from 'postgres';
 | 
			
		||||
import { citiesFile, excludePaths, IWorker } from 'src/constants';
 | 
			
		||||
import { Telemetry } from 'src/decorators';
 | 
			
		||||
import { EnvDto } from 'src/dtos/env.dto';
 | 
			
		||||
import { DatabaseExtension, ImmichEnvironment, ImmichHeader, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
 | 
			
		||||
import { QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseConnectionParams, VectorExtension } from 'src/repositories/database.repository';
 | 
			
		||||
import {
 | 
			
		||||
  DatabaseExtension,
 | 
			
		||||
  ImmichEnvironment,
 | 
			
		||||
  ImmichHeader,
 | 
			
		||||
  ImmichTelemetry,
 | 
			
		||||
  ImmichWorker,
 | 
			
		||||
  LogLevel,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { DatabaseConnectionParams, VectorExtension } from 'src/types';
 | 
			
		||||
import { setDifference } from 'src/utils/set';
 | 
			
		||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,66 +4,16 @@ import AsyncLock from 'async-lock';
 | 
			
		||||
import { Kysely, sql } from 'kysely';
 | 
			
		||||
import { InjectKysely } from 'nestjs-kysely';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
 | 
			
		||||
import { EXTENSION_NAMES, POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
 | 
			
		||||
import { DB } from 'src/db';
 | 
			
		||||
import { DatabaseExtension } from 'src/enum';
 | 
			
		||||
import { DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types';
 | 
			
		||||
import { UPSERT_COLUMNS } from 'src/utils/database';
 | 
			
		||||
import { isValidInteger } from 'src/validation';
 | 
			
		||||
import { DataSource, EntityManager, EntityMetadata, QueryRunner } from 'typeorm';
 | 
			
		||||
 | 
			
		||||
export type VectorExtension = DatabaseExtension.VECTOR | DatabaseExtension.VECTORS;
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionURL = {
 | 
			
		||||
  connectionType: 'url';
 | 
			
		||||
  url: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionParts = {
 | 
			
		||||
  connectionType: 'parts';
 | 
			
		||||
  host: string;
 | 
			
		||||
  port: number;
 | 
			
		||||
  username: string;
 | 
			
		||||
  password: string;
 | 
			
		||||
  database: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionParams = DatabaseConnectionURL | DatabaseConnectionParts;
 | 
			
		||||
 | 
			
		||||
export enum VectorIndex {
 | 
			
		||||
  CLIP = 'clip_index',
 | 
			
		||||
  FACE = 'face_index',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum DatabaseLock {
 | 
			
		||||
  GeodataImport = 100,
 | 
			
		||||
  Migrations = 200,
 | 
			
		||||
  SystemFileMounts = 300,
 | 
			
		||||
  StorageTemplateMigration = 420,
 | 
			
		||||
  VersionHistory = 500,
 | 
			
		||||
  CLIPDimSize = 512,
 | 
			
		||||
  Library = 1337,
 | 
			
		||||
  GetSystemConfig = 69,
 | 
			
		||||
  BackupDatabase = 42,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const EXTENSION_NAMES: Record<DatabaseExtension, string> = {
 | 
			
		||||
  cube: 'cube',
 | 
			
		||||
  earthdistance: 'earthdistance',
 | 
			
		||||
  vector: 'pgvector',
 | 
			
		||||
  vectors: 'pgvecto.rs',
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export interface ExtensionVersion {
 | 
			
		||||
  availableVersion: string | null;
 | 
			
		||||
  installedVersion: string | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface VectorUpdateResult {
 | 
			
		||||
  restartRequired: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class DatabaseRepository {
 | 
			
		||||
  private vectorExtension: VectorExtension;
 | 
			
		||||
 | 
			
		||||
@ -15,10 +15,10 @@ import { EventConfig } from 'src/decorators';
 | 
			
		||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
 | 
			
		||||
import { ImmichWorker, MetadataKey } from 'src/enum';
 | 
			
		||||
import { JobItem, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichWorker, MetadataKey, QueueName } from 'src/enum';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { JobItem } from 'src/types';
 | 
			
		||||
import { handlePromiseError } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
type EmitHandlers = Partial<{ [T in EmitEvent]: Array<EventItem<T>> }>;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
import { IJobRepository } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AccessRepository } from 'src/repositories/access.repository';
 | 
			
		||||
import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
			
		||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
 | 
			
		||||
@ -52,6 +51,7 @@ export const repositories = [
 | 
			
		||||
  CryptoRepository,
 | 
			
		||||
  DatabaseRepository,
 | 
			
		||||
  EventRepository,
 | 
			
		||||
  JobRepository,
 | 
			
		||||
  LibraryRepository,
 | 
			
		||||
  LoggingRepository,
 | 
			
		||||
  MachineLearningRepository,
 | 
			
		||||
@ -79,5 +79,3 @@ export const repositories = [
 | 
			
		||||
  ViewRepository,
 | 
			
		||||
  VersionHistoryRepository,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const providers = [{ provide: IJobRepository, useClass: JobRepository }];
 | 
			
		||||
 | 
			
		||||
@ -5,22 +5,11 @@ import { JobsOptions, Queue, Worker } from 'bullmq';
 | 
			
		||||
import { ClassConstructor } from 'class-transformer';
 | 
			
		||||
import { setTimeout } from 'node:timers/promises';
 | 
			
		||||
import { JobConfig } from 'src/decorators';
 | 
			
		||||
import { MetadataKey } from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  IEntityJob,
 | 
			
		||||
  IJobRepository,
 | 
			
		||||
  JobCounts,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobOf,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueCleanType,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  QueueStatus,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, MetadataKey, QueueCleanType, QueueName } from 'src/enum';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { EventRepository } from 'src/repositories/event.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { IEntityJob, JobCounts, JobItem, JobOf, QueueStatus } from 'src/types';
 | 
			
		||||
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
type JobMapItem = {
 | 
			
		||||
@ -31,7 +20,7 @@ type JobMapItem = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class JobRepository implements IJobRepository {
 | 
			
		||||
export class JobRepository {
 | 
			
		||||
  private workers: Partial<Record<QueueName, Worker>> = {};
 | 
			
		||||
  private handlers: Partial<Record<JobName, JobMapItem>> = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import { jsonArrayFrom } from 'kysely/helpers/postgres';
 | 
			
		||||
import { InjectKysely } from 'nestjs-kysely';
 | 
			
		||||
import { DB, Memories } from 'src/db';
 | 
			
		||||
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
 | 
			
		||||
import { IBulkAsset } from 'src/utils/asset.util';
 | 
			
		||||
import { IBulkAsset } from 'src/types';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class MemoryRepository implements IBulkAsset {
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,7 @@ import { AlbumUpdateEmail } from 'src/emails/album-update.email';
 | 
			
		||||
import { TestEmail } from 'src/emails/test.email';
 | 
			
		||||
import { WelcomeEmail } from 'src/emails/welcome.email';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
 | 
			
		||||
export type EmailImageAttachment = {
 | 
			
		||||
  filename: string;
 | 
			
		||||
  path: string;
 | 
			
		||||
  cid: string;
 | 
			
		||||
};
 | 
			
		||||
import { EmailImageAttachment } from 'src/types';
 | 
			
		||||
 | 
			
		||||
export type SendEmailOptions = {
 | 
			
		||||
  from: string;
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,7 @@ import { AssetMediaStatus, AssetRejectReason, AssetUploadAction } from 'src/dtos
 | 
			
		||||
import { AssetMediaCreateDto, AssetMediaReplaceDto, AssetMediaSize, UploadFieldName } from 'src/dtos/asset-media.dto';
 | 
			
		||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
 | 
			
		||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetFileType, AssetStatus, AssetType, CacheControl } from 'src/enum';
 | 
			
		||||
import { JobName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetFileType, AssetStatus, AssetType, CacheControl, JobName } from 'src/enum';
 | 
			
		||||
import { AuthRequest } from 'src/middleware/auth.guard';
 | 
			
		||||
import { AssetMediaService } from 'src/services/asset-media.service';
 | 
			
		||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
			
		||||
 | 
			
		||||
@ -21,29 +21,22 @@ import {
 | 
			
		||||
} from 'src/dtos/asset-media.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetStatus, AssetType, CacheControl, Permission, StorageFolder } from 'src/enum';
 | 
			
		||||
import { JobName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetStatus, AssetType, CacheControl, JobName, Permission, StorageFolder } from 'src/enum';
 | 
			
		||||
import { AuthRequest } from 'src/middleware/auth.guard';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { UploadFile } from 'src/types';
 | 
			
		||||
import { requireUploadAccess } from 'src/utils/access';
 | 
			
		||||
import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util';
 | 
			
		||||
import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file';
 | 
			
		||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
			
		||||
import { fromChecksum } from 'src/utils/request';
 | 
			
		||||
export interface UploadRequest {
 | 
			
		||||
 | 
			
		||||
interface UploadRequest {
 | 
			
		||||
  auth: AuthDto | null;
 | 
			
		||||
  fieldName: UploadFieldName;
 | 
			
		||||
  file: UploadFile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UploadFile {
 | 
			
		||||
  uuid: string;
 | 
			
		||||
  checksum: Buffer;
 | 
			
		||||
  originalPath: string;
 | 
			
		||||
  originalName: string;
 | 
			
		||||
  size: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AssetMediaService extends BaseService {
 | 
			
		||||
  async getUploadAssetIdByChecksum(auth: AuthDto, checksum?: string): Promise<AssetMediaResponseDto | undefined> {
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,7 @@ import { DateTime } from 'luxon';
 | 
			
		||||
import { mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetStatus, AssetType } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetStatus, AssetType, JobName, JobStatus } from 'src/enum';
 | 
			
		||||
import { AssetStats } from 'src/repositories/asset.repository';
 | 
			
		||||
import { AssetService } from 'src/services/asset.service';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { DateTime, Duration } from 'luxon';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { OnJob } from 'src/decorators';
 | 
			
		||||
import {
 | 
			
		||||
  AssetResponseDto,
 | 
			
		||||
@ -20,20 +21,13 @@ import {
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { MemoryLaneDto } from 'src/dtos/search.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetStatus, Permission } from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  ISidecarWriteJob,
 | 
			
		||||
  JOBS_ASSET_PAGINATION_SIZE,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobOf,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetStatus, JobName, JobStatus, Permission, QueueName } from 'src/enum';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { ISidecarWriteJob, JobItem, JobOf } from 'src/types';
 | 
			
		||||
import { getAssetFiles, getMyPartnerIds, onAfterUnlink, onBeforeLink, onBeforeUnlink } from 'src/utils/asset.util';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AssetService extends BaseService {
 | 
			
		||||
  async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> {
 | 
			
		||||
    const partnerIds = await getMyPartnerIds({
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,14 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { FileReportItemDto } from 'src/dtos/audit.dto';
 | 
			
		||||
import { AssetFileType, AssetPathType, DatabaseAction, EntityType, PersonPathType, UserPathType } from 'src/enum';
 | 
			
		||||
import { JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import {
 | 
			
		||||
  AssetFileType,
 | 
			
		||||
  AssetPathType,
 | 
			
		||||
  DatabaseAction,
 | 
			
		||||
  EntityType,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  PersonPathType,
 | 
			
		||||
  UserPathType,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { AuditService } from 'src/services/audit.service';
 | 
			
		||||
import { auditStub } from 'test/fixtures/audit.stub';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import { DateTime } from 'luxon';
 | 
			
		||||
import { resolve } from 'node:path';
 | 
			
		||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
 | 
			
		||||
import { AUDIT_LOG_MAX_DURATION, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnJob } from 'src/decorators';
 | 
			
		||||
import {
 | 
			
		||||
@ -17,12 +17,14 @@ import {
 | 
			
		||||
  AssetFileType,
 | 
			
		||||
  AssetPathType,
 | 
			
		||||
  DatabaseAction,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  Permission,
 | 
			
		||||
  PersonPathType,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  StorageFolder,
 | 
			
		||||
  UserPathType,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { JobName, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
import { PassThrough } from 'node:stream';
 | 
			
		||||
import { defaults, SystemConfig } from 'src/config';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { ImmichWorker, StorageFolder } from 'src/enum';
 | 
			
		||||
import { JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichWorker, JobStatus, StorageFolder } from 'src/enum';
 | 
			
		||||
import { BackupService } from 'src/services/backup.service';
 | 
			
		||||
import { systemConfigStub } from 'test/fixtures/system-config.stub';
 | 
			
		||||
import { mockSpawn, newTestService, ServiceMocks } from 'test/utils';
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,7 @@ import { default as path } from 'node:path';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { ImmichWorker, StorageFolder } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName, StorageFolder } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { handlePromiseError } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { BadRequestException, Inject } from '@nestjs/common';
 | 
			
		||||
import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import { Insertable } from 'kysely';
 | 
			
		||||
import sanitize from 'sanitize-filename';
 | 
			
		||||
import { SystemConfig } from 'src/config';
 | 
			
		||||
@ -6,7 +6,6 @@ import { SALT_ROUNDS } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { Users } from 'src/db';
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
import { IJobRepository } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AccessRepository } from 'src/repositories/access.repository';
 | 
			
		||||
import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
			
		||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
 | 
			
		||||
@ -19,6 +18,7 @@ import { CronRepository } from 'src/repositories/cron.repository';
 | 
			
		||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
 | 
			
		||||
import { DatabaseRepository } from 'src/repositories/database.repository';
 | 
			
		||||
import { EventRepository } from 'src/repositories/event.repository';
 | 
			
		||||
import { JobRepository } from 'src/repositories/job.repository';
 | 
			
		||||
import { LibraryRepository } from 'src/repositories/library.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { MachineLearningRepository } from 'src/repositories/machine-learning.repository';
 | 
			
		||||
@ -48,6 +48,7 @@ import { ViewRepository } from 'src/repositories/view-repository';
 | 
			
		||||
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
 | 
			
		||||
import { getConfig, updateConfig } from 'src/utils/config';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class BaseService {
 | 
			
		||||
  protected storageCore: StorageCore;
 | 
			
		||||
 | 
			
		||||
@ -64,7 +65,7 @@ export class BaseService {
 | 
			
		||||
    protected cryptoRepository: CryptoRepository,
 | 
			
		||||
    protected databaseRepository: DatabaseRepository,
 | 
			
		||||
    protected eventRepository: EventRepository,
 | 
			
		||||
    @Inject(IJobRepository) protected jobRepository: IJobRepository,
 | 
			
		||||
    protected jobRepository: JobRepository,
 | 
			
		||||
    protected keyRepository: ApiKeyRepository,
 | 
			
		||||
    protected libraryRepository: LibraryRepository,
 | 
			
		||||
    protected machineLearningRepository: MachineLearningRepository,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import { EXTENSION_NAMES } from 'src/constants';
 | 
			
		||||
import { DatabaseExtension } from 'src/enum';
 | 
			
		||||
import { EXTENSION_NAMES, VectorExtension } from 'src/repositories/database.repository';
 | 
			
		||||
import { DatabaseService } from 'src/services/database.service';
 | 
			
		||||
import { VectorExtension } from 'src/types';
 | 
			
		||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
 | 
			
		||||
import { newTestService, ServiceMocks } from 'test/utils';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,11 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { Duration } from 'luxon';
 | 
			
		||||
import semver from 'semver';
 | 
			
		||||
import { EXTENSION_NAMES } from 'src/constants';
 | 
			
		||||
import { OnEvent } from 'src/decorators';
 | 
			
		||||
import { BootstrapEventPriority, DatabaseExtension } from 'src/enum';
 | 
			
		||||
import { DatabaseLock, EXTENSION_NAMES, VectorExtension, VectorIndex } from 'src/repositories/database.repository';
 | 
			
		||||
import { BootstrapEventPriority, DatabaseExtension, DatabaseLock, VectorIndex } from 'src/enum';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { VectorExtension } from 'src/types';
 | 
			
		||||
 | 
			
		||||
type CreateFailedArgs = { name: string; extension: string; otherName: string };
 | 
			
		||||
type UpdateFailedArgs = { name: string; extension: string; availableVersion: string };
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { DuplicateService } from 'src/services/duplicate.service';
 | 
			
		||||
import { SearchService } from 'src/services/search.service';
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,15 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { OnJob } from 'src/decorators';
 | 
			
		||||
import { mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { AssetDuplicateResult } from 'src/repositories/search.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { isDuplicateDetectionEnabled } from 'src/utils/misc';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { defaults, SystemConfig } from 'src/config';
 | 
			
		||||
import { ImmichWorker } from 'src/enum';
 | 
			
		||||
import { JobCommand, JobItem, JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichWorker, JobCommand, JobName, JobStatus, QueueName } from 'src/enum';
 | 
			
		||||
import { JobService } from 'src/services/job.service';
 | 
			
		||||
import { JobItem } from 'src/types';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
import { newTestService, ServiceMocks } from 'test/utils';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,18 +3,19 @@ import { snakeCase } from 'lodash';
 | 
			
		||||
import { OnEvent } from 'src/decorators';
 | 
			
		||||
import { mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto';
 | 
			
		||||
import { AssetType, ImmichWorker, ManualJobName } from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  ConcurrentQueueName,
 | 
			
		||||
  AssetType,
 | 
			
		||||
  ImmichWorker,
 | 
			
		||||
  JobCommand,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  ManualJobName,
 | 
			
		||||
  QueueCleanType,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { ArgOf, ArgsOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { ConcurrentQueueName, JobItem } from 'src/types';
 | 
			
		||||
 | 
			
		||||
const asJobItem = (dto: JobCreateDto): JobItem => {
 | 
			
		||||
  switch (dto.name) {
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,12 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { Stats } from 'node:fs';
 | 
			
		||||
import { defaults, SystemConfig } from 'src/config';
 | 
			
		||||
import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { mapLibrary } from 'src/dtos/library.dto';
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
import { AssetType, ImmichWorker } from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  ILibraryAssetJob,
 | 
			
		||||
  ILibraryFileJob,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JOBS_LIBRARY_PAGINATION_SIZE,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetType, ImmichWorker, JobName, JobStatus } from 'src/enum';
 | 
			
		||||
import { LibraryService } from 'src/services/library.service';
 | 
			
		||||
import { ILibraryAssetJob, ILibraryFileJob } from 'src/types';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
import { libraryStub } from 'test/fixtures/library.stub';
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import { R_OK } from 'node:constants';
 | 
			
		||||
import path, { basename, isAbsolute, parse } from 'node:path';
 | 
			
		||||
import picomatch from 'picomatch';
 | 
			
		||||
import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import {
 | 
			
		||||
@ -16,11 +17,10 @@ import {
 | 
			
		||||
} from 'src/dtos/library.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { LibraryEntity } from 'src/entities/library.entity';
 | 
			
		||||
import { AssetType, ImmichWorker } from 'src/enum';
 | 
			
		||||
import { JobName, JobOf, JOBS_LIBRARY_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { AssetType, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
			
		||||
import { handlePromiseError } from 'src/utils/misc';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { MapMarkerDto, MapMarkerResponseDto, MapReverseGeocodeDto } from 'src/dtos/map.dto';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { getMyPartnerIds } from 'src/utils/asset.util';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class MapService extends BaseService {
 | 
			
		||||
  async getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise<MapMarkerResponseDto[]> {
 | 
			
		||||
    const userIds = [auth.user.id];
 | 
			
		||||
 | 
			
		||||
@ -9,14 +9,15 @@ import {
 | 
			
		||||
  AudioCodec,
 | 
			
		||||
  Colorspace,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  TranscodeHWAccel,
 | 
			
		||||
  TranscodePolicy,
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { JobCounts, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { MediaService } from 'src/services/media.service';
 | 
			
		||||
import { RawImageInfo } from 'src/types';
 | 
			
		||||
import { JobCounts, RawImageInfo } from 'src/types';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
import { faceStub } from 'test/fixtures/face.stub';
 | 
			
		||||
import { probeStub } from 'test/fixtures/media.stub';
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { dirname } from 'node:path';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
@ -10,7 +11,10 @@ import {
 | 
			
		||||
  AssetType,
 | 
			
		||||
  AudioCodec,
 | 
			
		||||
  Colorspace,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  LogLevel,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  StorageFolder,
 | 
			
		||||
  TranscodeHWAccel,
 | 
			
		||||
  TranscodePolicy,
 | 
			
		||||
@ -18,17 +22,9 @@ import {
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
  VideoContainer,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  JOBS_ASSET_PAGINATION_SIZE,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobOf,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { UpsertFileOptions, WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { AudioStreamInfo, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/types';
 | 
			
		||||
import { AudioStreamInfo, JobItem, JobOf, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/types';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { BaseConfig, ThumbnailConfig } from 'src/utils/media';
 | 
			
		||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,7 @@ import { Stats } from 'node:fs';
 | 
			
		||||
import { constants } from 'node:fs/promises';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { ExifEntity } from 'src/entities/exif.entity';
 | 
			
		||||
import { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetType, ExifOrientation, ImmichWorker, JobName, JobStatus, SourceType } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { ImmichTags } from 'src/repositories/metadata.repository';
 | 
			
		||||
import { MetadataService } from 'src/services/metadata.service';
 | 
			
		||||
 | 
			
		||||
@ -7,20 +7,29 @@ import { Duration } from 'luxon';
 | 
			
		||||
import { constants } from 'node:fs/promises';
 | 
			
		||||
import path from 'node:path';
 | 
			
		||||
import { SystemConfig } from 'src/config';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { Exif } from 'src/db';
 | 
			
		||||
import { OnEvent, OnJob } 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 { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
 | 
			
		||||
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import {
 | 
			
		||||
  AssetType,
 | 
			
		||||
  DatabaseLock,
 | 
			
		||||
  ExifOrientation,
 | 
			
		||||
  ImmichWorker,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  SourceType,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { ReverseGeocodeResult } from 'src/repositories/map.repository';
 | 
			
		||||
import { ImmichTags } from 'src/repositories/metadata.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { isFaceImportEnabled } from 'src/utils/misc';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
import { upsertTags } from 'src/utils/tag';
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,10 @@ import { defaults, SystemConfig } from 'src/config';
 | 
			
		||||
import { SystemConfigDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
 | 
			
		||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
 | 
			
		||||
import { AssetFileType, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { INotifyAlbumUpdateJob, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { EmailTemplate } from 'src/repositories/notification.repository';
 | 
			
		||||
import { NotificationService } from 'src/services/notification.service';
 | 
			
		||||
import { INotifyAlbumUpdateJob } from 'src/types';
 | 
			
		||||
import { albumStub } from 'test/fixtures/album.stub';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
import { userStub } from 'test/fixtures/user.stub';
 | 
			
		||||
 | 
			
		||||
@ -2,18 +2,11 @@ import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { AlbumEntity } from 'src/entities/album.entity';
 | 
			
		||||
import {
 | 
			
		||||
  IEntityJob,
 | 
			
		||||
  INotifyAlbumUpdateJob,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobOf,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { EmailImageAttachment, EmailTemplate } from 'src/repositories/notification.repository';
 | 
			
		||||
import { EmailTemplate } from 'src/repositories/notification.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { EmailImageAttachment, IEntityJob, INotifyAlbumUpdateJob, JobItem, JobOf } from 'src/types';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { getFilenameExtension } from 'src/utils/file';
 | 
			
		||||
import { getExternalDomain } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,7 @@ import { BadRequestException, NotFoundException } from '@nestjs/common';
 | 
			
		||||
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
 | 
			
		||||
import { mapFaces, mapPerson, PersonResponseDto } from 'src/dtos/person.dto';
 | 
			
		||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
 | 
			
		||||
import { CacheControl, Colorspace, ImageFormat, SourceType, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { CacheControl, Colorspace, ImageFormat, JobName, JobStatus, SourceType, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { DetectedFaces } from 'src/repositories/machine-learning.repository';
 | 
			
		||||
import { FaceSearchResult } from 'src/repositories/search.repository';
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
 | 
			
		||||
import { FACE_THUMBNAIL_SIZE } from 'src/constants';
 | 
			
		||||
import { FACE_THUMBNAIL_SIZE, JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnJob } from 'src/decorators';
 | 
			
		||||
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
 | 
			
		||||
@ -27,24 +27,19 @@ import {
 | 
			
		||||
  AssetType,
 | 
			
		||||
  CacheControl,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  Permission,
 | 
			
		||||
  PersonPathType,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  SourceType,
 | 
			
		||||
  SystemMetadataKey,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  JOBS_ASSET_PAGINATION_SIZE,
 | 
			
		||||
  JobItem,
 | 
			
		||||
  JobName,
 | 
			
		||||
  JobOf,
 | 
			
		||||
  JobStatus,
 | 
			
		||||
  QueueName,
 | 
			
		||||
} from 'src/interfaces/job.interface';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { BoundingBox } from 'src/repositories/machine-learning.repository';
 | 
			
		||||
import { UpdateFacesData } from 'src/repositories/person.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { CropOptions, ImageDimensions, InputDimensions } from 'src/types';
 | 
			
		||||
import { CropOptions, ImageDimensions, InputDimensions, JobItem, JobOf } from 'src/types';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
			
		||||
import { mimeTypes } from 'src/utils/mime-types';
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobStatus } from 'src/enum';
 | 
			
		||||
import { SessionService } from 'src/services/session.service';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
import { sessionStub } from 'test/fixtures/session.stub';
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,7 @@ import { DateTime } from 'luxon';
 | 
			
		||||
import { OnJob } from 'src/decorators';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { SessionResponseDto, mapSession } from 'src/dtos/session.dto';
 | 
			
		||||
import { Permission } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, Permission, QueueName } from 'src/enum';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
import { SystemConfig } from 'src/config';
 | 
			
		||||
import { ImmichWorker } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichWorker, JobName, JobStatus } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { SmartInfoService } from 'src/services/smart-info.service';
 | 
			
		||||
import { getCLIPModelInfo } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,12 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { SystemConfig } from 'src/config';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { ImmichWorker } from 'src/enum';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum';
 | 
			
		||||
import { WithoutProperty } from 'src/repositories/asset.repository';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { getAssetFiles } from 'src/utils/asset.util';
 | 
			
		||||
import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
import { Stats } from 'node:fs';
 | 
			
		||||
import { defaults, SystemConfig } from 'src/config';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetPathType } from 'src/enum';
 | 
			
		||||
import { JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { AssetPathType, JobStatus } from 'src/enum';
 | 
			
		||||
import { StorageTemplateService } from 'src/services/storage-template.service';
 | 
			
		||||
import { albumStub } from 'test/fixtures/album.stub';
 | 
			
		||||
import { assetStub } from 'test/fixtures/asset.stub';
 | 
			
		||||
 | 
			
		||||
@ -3,15 +3,15 @@ import handlebar from 'handlebars';
 | 
			
		||||
import { DateTime } from 'luxon';
 | 
			
		||||
import path from 'node:path';
 | 
			
		||||
import sanitize from 'sanitize-filename';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { AssetPathType, AssetType, StorageFolder } from 'src/enum';
 | 
			
		||||
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { AssetPathType, AssetType, DatabaseLock, JobName, JobStatus, QueueName, StorageFolder } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { getLivePhotoMotionFilename } from 'src/utils/file';
 | 
			
		||||
import { usePagination } from 'src/utils/pagination';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,9 @@ import { join } from 'node:path';
 | 
			
		||||
import { StorageCore } from 'src/cores/storage.core';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { SystemFlags } from 'src/entities/system-metadata.entity';
 | 
			
		||||
import { StorageFolder, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { ImmichStartupError } from 'src/utils/misc';
 | 
			
		||||
 | 
			
		||||
const docsMessage = `Please see https://immich.app/docs/administration/system-integrity#folder-checks for more information.`;
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { DateTime } from 'luxon';
 | 
			
		||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
 | 
			
		||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
@ -10,6 +11,7 @@ import { setIsEqual } from 'src/utils/set';
 | 
			
		||||
 | 
			
		||||
const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] };
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class SyncService extends BaseService {
 | 
			
		||||
  async getFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
 | 
			
		||||
    // mobile implementation is faster if this is a single id
 | 
			
		||||
 | 
			
		||||
@ -6,13 +6,13 @@ import {
 | 
			
		||||
  CQMode,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  LogLevel,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  ToneMapping,
 | 
			
		||||
  TranscodeHWAccel,
 | 
			
		||||
  TranscodePolicy,
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
  VideoContainer,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { SystemConfigService } from 'src/services/system-config.service';
 | 
			
		||||
import { DeepPartial } from 'src/types';
 | 
			
		||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import { instanceToPlain } from 'class-transformer';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { defaults } from 'src/config';
 | 
			
		||||
import { OnEvent } from 'src/decorators';
 | 
			
		||||
import { SystemConfigDto, mapConfig } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { mapConfig, SystemConfigDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { BootstrapEventPriority } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
 | 
			
		||||
import { JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobStatus } from 'src/enum';
 | 
			
		||||
import { TagService } from 'src/services/tag.service';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
import { tagResponseStub, tagStub } from 'test/fixtures/tag.stub';
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,7 @@ import {
 | 
			
		||||
  mapTag,
 | 
			
		||||
} from 'src/dtos/tag.dto';
 | 
			
		||||
import { TagEntity } from 'src/entities/tag.entity';
 | 
			
		||||
import { Permission } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, Permission, QueueName } from 'src/enum';
 | 
			
		||||
import { AssetTagItem } from 'src/repositories/tag.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { BadRequestException, Injectable } from '@nestjs/common';
 | 
			
		||||
import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
 | 
			
		||||
@ -7,6 +7,7 @@ import { TimeBucketOptions } from 'src/repositories/asset.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { getMyPartnerIds } from 'src/utils/asset.util';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class TimelineService extends BaseService {
 | 
			
		||||
  async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
 | 
			
		||||
    await this.timeBucketChecks(auth, dto);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { BadRequestException } from '@nestjs/common';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus } from 'src/enum';
 | 
			
		||||
import { TrashService } from 'src/services/trash.service';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
import { newTestService, ServiceMocks } from 'test/utils';
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { TrashResponseDto } from 'src/dtos/trash.dto';
 | 
			
		||||
import { Permission } from 'src/enum';
 | 
			
		||||
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, JobStatus, Permission, QueueName } from 'src/enum';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class TrashService extends BaseService {
 | 
			
		||||
  async restoreAssets(auth: AuthDto, dto: BulkIdsDto): Promise<TrashResponseDto> {
 | 
			
		||||
    const { ids } = dto;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
			
		||||
import { mapUserAdmin } from 'src/dtos/user.dto';
 | 
			
		||||
import { UserStatus } from 'src/enum';
 | 
			
		||||
import { JobName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, UserStatus } from 'src/enum';
 | 
			
		||||
import { UserAdminService } from 'src/services/user-admin.service';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
import { userStub } from 'test/fixtures/user.stub';
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,7 @@ import {
 | 
			
		||||
  UserAdminUpdateDto,
 | 
			
		||||
  mapUserAdmin,
 | 
			
		||||
} from 'src/dtos/user.dto';
 | 
			
		||||
import { UserMetadataKey, UserStatus } from 'src/enum';
 | 
			
		||||
import { JobName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobName, UserMetadataKey, UserStatus } from 'src/enum';
 | 
			
		||||
import { UserFindOptions } from 'src/repositories/user.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import { BadRequestException, InternalServerErrorException, NotFoundException } from '@nestjs/common';
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
import { CacheControl, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { CacheControl, JobName, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { UserService } from 'src/services/user.service';
 | 
			
		||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
			
		||||
import { authStub } from 'test/fixtures/auth.stub';
 | 
			
		||||
 | 
			
		||||
@ -10,10 +10,10 @@ import { CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
 | 
			
		||||
import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto';
 | 
			
		||||
import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
import { CacheControl, StorageFolder, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { CacheControl, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum';
 | 
			
		||||
import { UserFindOptions } from 'src/repositories/user.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
import { JobOf } from 'src/types';
 | 
			
		||||
import { ImmichFileResponse } from 'src/utils/file';
 | 
			
		||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
import { DateTime } from 'luxon';
 | 
			
		||||
import { SemVer } from 'semver';
 | 
			
		||||
import { serverVersion } from 'src/constants';
 | 
			
		||||
import { ImmichEnvironment, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
			
		||||
import { ImmichEnvironment, JobName, JobStatus, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { VersionService } from 'src/services/version.service';
 | 
			
		||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
 | 
			
		||||
import { newTestService, ServiceMocks } from 'test/utils';
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,7 @@ import { serverVersion } from 'src/constants';
 | 
			
		||||
import { OnEvent, OnJob } from 'src/decorators';
 | 
			
		||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
 | 
			
		||||
import { VersionCheckMetadata } from 'src/entities/system-metadata.entity';
 | 
			
		||||
import { ImmichEnvironment, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { DatabaseLock, ImmichEnvironment, JobName, JobStatus, QueueName, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { ArgOf } from 'src/repositories/event.repository';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
 | 
			
		||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { AssetEntity } from 'src/entities/asset.entity';
 | 
			
		||||
import { BaseService } from 'src/services/base.service';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class ViewService extends BaseService {
 | 
			
		||||
  getUniqueOriginalPaths(auth: AuthDto): Promise<string[]> {
 | 
			
		||||
    return this.viewRepository.getUniqueOriginalPaths(auth.user.id);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,14 @@
 | 
			
		||||
import { UserEntity } from 'src/entities/user.entity';
 | 
			
		||||
import { ExifOrientation, ImageFormat, Permission, TranscodeTarget, VideoCodec } from 'src/enum';
 | 
			
		||||
import {
 | 
			
		||||
  DatabaseExtension,
 | 
			
		||||
  ExifOrientation,
 | 
			
		||||
  ImageFormat,
 | 
			
		||||
  JobName,
 | 
			
		||||
  Permission,
 | 
			
		||||
  QueueName,
 | 
			
		||||
  TranscodeTarget,
 | 
			
		||||
  VideoCodec,
 | 
			
		||||
} from 'src/enum';
 | 
			
		||||
import { ActivityRepository } from 'src/repositories/activity.repository';
 | 
			
		||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
 | 
			
		||||
import { MemoryRepository } from 'src/repositories/memory.repository';
 | 
			
		||||
@ -167,3 +176,245 @@ export interface VideoInterfaces {
 | 
			
		||||
  dri: string[];
 | 
			
		||||
  mali: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type ConcurrentQueueName = Exclude<
 | 
			
		||||
  QueueName,
 | 
			
		||||
  | QueueName.STORAGE_TEMPLATE_MIGRATION
 | 
			
		||||
  | QueueName.FACIAL_RECOGNITION
 | 
			
		||||
  | QueueName.DUPLICATE_DETECTION
 | 
			
		||||
  | QueueName.BACKUP_DATABASE
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
export type Jobs = { [K in JobItem['name']]: (JobItem & { name: K })['data'] };
 | 
			
		||||
export type JobOf<T extends JobName> = Jobs[T];
 | 
			
		||||
 | 
			
		||||
export interface IBaseJob {
 | 
			
		||||
  force?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDelayedJob extends IBaseJob {
 | 
			
		||||
  /** The minimum time to wait to execute this job, in milliseconds. */
 | 
			
		||||
  delay?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEntityJob extends IBaseJob {
 | 
			
		||||
  id: string;
 | 
			
		||||
  source?: 'upload' | 'sidecar-write' | 'copy';
 | 
			
		||||
  notify?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IAssetDeleteJob extends IEntityJob {
 | 
			
		||||
  deleteOnDisk: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILibraryFileJob extends IEntityJob {
 | 
			
		||||
  ownerId: string;
 | 
			
		||||
  assetPath: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILibraryAssetJob extends IEntityJob {
 | 
			
		||||
  importPaths: string[];
 | 
			
		||||
  exclusionPatterns: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
  rating?: number;
 | 
			
		||||
  tags?: true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDeferrableJob extends IEntityJob {
 | 
			
		||||
  deferred?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INightlyJob extends IBaseJob {
 | 
			
		||||
  nightly?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type EmailImageAttachment = {
 | 
			
		||||
  filename: string;
 | 
			
		||||
  path: string;
 | 
			
		||||
  cid: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface IEmailJob {
 | 
			
		||||
  to: string;
 | 
			
		||||
  subject: string;
 | 
			
		||||
  html: string;
 | 
			
		||||
  text: string;
 | 
			
		||||
  imageAttachments?: EmailImageAttachment[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifySignupJob extends IEntityJob {
 | 
			
		||||
  tempPassword?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifyAlbumInviteJob extends IEntityJob {
 | 
			
		||||
  recipientId: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface INotifyAlbumUpdateJob extends IEntityJob, IDelayedJob {
 | 
			
		||||
  recipientIds: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface JobCounts {
 | 
			
		||||
  active: number;
 | 
			
		||||
  completed: number;
 | 
			
		||||
  failed: number;
 | 
			
		||||
  delayed: number;
 | 
			
		||||
  waiting: number;
 | 
			
		||||
  paused: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface QueueStatus {
 | 
			
		||||
  isActive: boolean;
 | 
			
		||||
  isPaused: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type JobItem =
 | 
			
		||||
  // Backups
 | 
			
		||||
  | { name: JobName.BACKUP_DATABASE; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Transcoding
 | 
			
		||||
  | { name: JobName.QUEUE_VIDEO_CONVERSION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.VIDEO_CONVERSION; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Thumbnails
 | 
			
		||||
  | { name: JobName.QUEUE_GENERATE_THUMBNAILS; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.GENERATE_THUMBNAILS; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // User
 | 
			
		||||
  | { name: JobName.USER_DELETE_CHECK; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.USER_DELETION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.USER_SYNC_USAGE; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Storage Template
 | 
			
		||||
  | { name: JobName.STORAGE_TEMPLATE_MIGRATION; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Migration
 | 
			
		||||
  | { name: JobName.QUEUE_MIGRATION; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.MIGRATE_ASSET; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.MIGRATE_PERSON; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Metadata Extraction
 | 
			
		||||
  | { name: JobName.QUEUE_METADATA_EXTRACTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.METADATA_EXTRACTION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LINK_LIVE_PHOTOS; data: IEntityJob }
 | 
			
		||||
  // Sidecar Scanning
 | 
			
		||||
  | { name: JobName.QUEUE_SIDECAR; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_DISCOVERY; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_SYNC; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.SIDECAR_WRITE; data: ISidecarWriteJob }
 | 
			
		||||
 | 
			
		||||
  // Facial Recognition
 | 
			
		||||
  | { name: JobName.QUEUE_FACE_DETECTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.FACE_DETECTION; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.QUEUE_FACIAL_RECOGNITION; data: INightlyJob }
 | 
			
		||||
  | { name: JobName.FACIAL_RECOGNITION; data: IDeferrableJob }
 | 
			
		||||
  | { name: JobName.GENERATE_PERSON_THUMBNAIL; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Smart Search
 | 
			
		||||
  | { name: JobName.QUEUE_SMART_SEARCH; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.SMART_SEARCH; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.QUEUE_TRASH_EMPTY; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Duplicate Detection
 | 
			
		||||
  | { name: JobName.QUEUE_DUPLICATE_DETECTION; data: IBaseJob }
 | 
			
		||||
  | { name: JobName.DUPLICATE_DETECTION; data: IEntityJob }
 | 
			
		||||
 | 
			
		||||
  // Filesystem
 | 
			
		||||
  | { name: JobName.DELETE_FILES; data: IDeleteFilesJob }
 | 
			
		||||
 | 
			
		||||
  // Cleanup
 | 
			
		||||
  | { name: JobName.CLEAN_OLD_AUDIT_LOGS; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.CLEAN_OLD_SESSION_TOKENS; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Tags
 | 
			
		||||
  | { name: JobName.TAG_CLEANUP; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Asset Deletion
 | 
			
		||||
  | { name: JobName.PERSON_CLEANUP; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.ASSET_DELETION; data: IAssetDeleteJob }
 | 
			
		||||
  | { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Library Management
 | 
			
		||||
  | { name: JobName.LIBRARY_SYNC_FILE; data: ILibraryFileJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_FILES; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_ASSETS; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_SYNC_ASSET; data: ILibraryAssetJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_DELETE; data: IEntityJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_SYNC_ALL; data?: IBaseJob }
 | 
			
		||||
  | { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
 | 
			
		||||
 | 
			
		||||
  // Notification
 | 
			
		||||
  | { name: JobName.SEND_EMAIL; data: IEmailJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_ALBUM_INVITE; data: INotifyAlbumInviteJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_ALBUM_UPDATE; data: INotifyAlbumUpdateJob }
 | 
			
		||||
  | { name: JobName.NOTIFY_SIGNUP; data: INotifySignupJob }
 | 
			
		||||
 | 
			
		||||
  // Version check
 | 
			
		||||
  | { name: JobName.VERSION_CHECK; data: IBaseJob };
 | 
			
		||||
 | 
			
		||||
export type VectorExtension = DatabaseExtension.VECTOR | DatabaseExtension.VECTORS;
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionURL = {
 | 
			
		||||
  connectionType: 'url';
 | 
			
		||||
  url: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionParts = {
 | 
			
		||||
  connectionType: 'parts';
 | 
			
		||||
  host: string;
 | 
			
		||||
  port: number;
 | 
			
		||||
  username: string;
 | 
			
		||||
  password: string;
 | 
			
		||||
  database: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type DatabaseConnectionParams = DatabaseConnectionURL | DatabaseConnectionParts;
 | 
			
		||||
 | 
			
		||||
export interface ExtensionVersion {
 | 
			
		||||
  availableVersion: string | null;
 | 
			
		||||
  installedVersion: string | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface VectorUpdateResult {
 | 
			
		||||
  restartRequired: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ImmichFile extends Express.Multer.File {
 | 
			
		||||
  /** sha1 hash of file */
 | 
			
		||||
  uuid: string;
 | 
			
		||||
  checksum: Buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UploadFile {
 | 
			
		||||
  uuid: string;
 | 
			
		||||
  checksum: Buffer;
 | 
			
		||||
  originalPath: string;
 | 
			
		||||
  originalName: string;
 | 
			
		||||
  size: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UploadFiles {
 | 
			
		||||
  assetData: ImmichFile[];
 | 
			
		||||
  sidecarData: ImmichFile[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IBulkAsset {
 | 
			
		||||
  getAssetIds: (id: string, assetIds: string[]) => Promise<Set<string>>;
 | 
			
		||||
  addAssetIds: (id: string, assetIds: string[]) => Promise<void>;
 | 
			
		||||
  removeAssetIds: (id: string, assetIds: string[]) => Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,20 +6,13 @@ import { AuthDto } from 'src/dtos/auth.dto';
 | 
			
		||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
 | 
			
		||||
import { AssetFileType, AssetType, Permission } from 'src/enum';
 | 
			
		||||
import { AuthRequest } from 'src/middleware/auth.guard';
 | 
			
		||||
import { ImmichFile } from 'src/middleware/file-upload.interceptor';
 | 
			
		||||
import { AccessRepository } from 'src/repositories/access.repository';
 | 
			
		||||
import { AssetRepository } from 'src/repositories/asset.repository';
 | 
			
		||||
import { EventRepository } from 'src/repositories/event.repository';
 | 
			
		||||
import { PartnerRepository } from 'src/repositories/partner.repository';
 | 
			
		||||
import { UploadFile } from 'src/services/asset-media.service';
 | 
			
		||||
import { IBulkAsset, ImmichFile, UploadFile } from 'src/types';
 | 
			
		||||
import { checkAccess } from 'src/utils/access';
 | 
			
		||||
 | 
			
		||||
export interface IBulkAsset {
 | 
			
		||||
  getAssetIds: (id: string, assetIds: string[]) => Promise<Set<string>>;
 | 
			
		||||
  addAssetIds: (id: string, assetIds: string[]) => Promise<void>;
 | 
			
		||||
  removeAssetIds: (id: string, assetIds: string[]) => Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getFileByType = (files: AssetFileEntity[] | undefined, type: AssetFileType) => {
 | 
			
		||||
  return (files || []).find((file) => file.type === type);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,8 @@ import { load as loadYaml } from 'js-yaml';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
import { SystemConfig, defaults } from 'src/config';
 | 
			
		||||
import { SystemConfigDto } from 'src/dtos/system-config.dto';
 | 
			
		||||
import { SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { DatabaseLock, SystemMetadataKey } from 'src/enum';
 | 
			
		||||
import { ConfigRepository } from 'src/repositories/config.repository';
 | 
			
		||||
import { DatabaseLock } from 'src/repositories/database.repository';
 | 
			
		||||
import { LoggingRepository } from 'src/repositories/logging.repository';
 | 
			
		||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
 | 
			
		||||
import { DeepPartial } from 'src/types';
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
import { IJobRepository } from 'src/interfaces/job.interface';
 | 
			
		||||
import { JobRepository } from 'src/repositories/job.repository';
 | 
			
		||||
import { RepositoryInterface } from 'src/types';
 | 
			
		||||
import { Mocked, vitest } from 'vitest';
 | 
			
		||||
 | 
			
		||||
export const newJobRepositoryMock = (): Mocked<IJobRepository> => {
 | 
			
		||||
export const newJobRepositoryMock = (): Mocked<RepositoryInterface<JobRepository>> => {
 | 
			
		||||
  return {
 | 
			
		||||
    setup: vitest.fn(),
 | 
			
		||||
    startWorkers: vitest.fn(),
 | 
			
		||||
 | 
			
		||||
@ -194,12 +194,12 @@ export const newTestService = <T extends BaseService>(
 | 
			
		||||
    albumMock as RepositoryInterface<AlbumRepository> as AlbumRepository,
 | 
			
		||||
    albumUserMock as RepositoryInterface<AlbumUserRepository> as AlbumUserRepository,
 | 
			
		||||
    assetMock as RepositoryInterface<AssetRepository> as AssetRepository,
 | 
			
		||||
    configMock,
 | 
			
		||||
    configMock as RepositoryInterface<ConfigRepository> as ConfigRepository,
 | 
			
		||||
    cronMock as RepositoryInterface<CronRepository> as CronRepository,
 | 
			
		||||
    cryptoMock as RepositoryInterface<CryptoRepository> as CryptoRepository,
 | 
			
		||||
    databaseMock as RepositoryInterface<DatabaseRepository> as DatabaseRepository,
 | 
			
		||||
    eventMock as RepositoryInterface<EventRepository> as EventRepository,
 | 
			
		||||
    jobMock,
 | 
			
		||||
    jobMock as RepositoryInterface<JobRepository> as JobRepository,
 | 
			
		||||
    apiKeyMock as RepositoryInterface<ApiKeyRepository> as ApiKeyRepository,
 | 
			
		||||
    libraryMock as RepositoryInterface<LibraryRepository> as LibraryRepository,
 | 
			
		||||
    machineLearningMock as RepositoryInterface<MachineLearningRepository> as MachineLearningRepository,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user