mirror of
https://github.com/immich-app/immich.git
synced 2025-06-04 22:24:26 -04:00
refactor: repositories (#16038)
This commit is contained in:
parent
9d85272c2b
commit
5f3a42a132
@ -13,7 +13,6 @@ import { IWorker } from 'src/constants';
|
|||||||
import { controllers } from 'src/controllers';
|
import { controllers } from 'src/controllers';
|
||||||
import { entities } from 'src/entities';
|
import { entities } from 'src/entities';
|
||||||
import { ImmichWorker } from 'src/enum';
|
import { ImmichWorker } from 'src/enum';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||||
import { AuthGuard } from 'src/middleware/auth.guard';
|
import { AuthGuard } from 'src/middleware/auth.guard';
|
||||||
import { ErrorInterceptor } from 'src/middleware/error.interceptor';
|
import { ErrorInterceptor } from 'src/middleware/error.interceptor';
|
||||||
@ -22,9 +21,11 @@ import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
|
|||||||
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
|
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
|
||||||
import { providers, repositories } from 'src/repositories';
|
import { providers, repositories } from 'src/repositories';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository';
|
import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository';
|
||||||
import { services } from 'src/services';
|
import { services } from 'src/services';
|
||||||
|
import { AuthService } from 'src/services/auth.service';
|
||||||
import { CliService } from 'src/services/cli.service';
|
import { CliService } from 'src/services/cli.service';
|
||||||
import { DatabaseService } from 'src/services/database.service';
|
import { DatabaseService } from 'src/services/database.service';
|
||||||
|
|
||||||
@ -78,9 +79,10 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(IWorker) private worker: ImmichWorker,
|
@Inject(IWorker) private worker: ImmichWorker,
|
||||||
logger: LoggingRepository,
|
logger: LoggingRepository,
|
||||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
private eventRepository: EventRepository,
|
||||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||||
private telemetryRepository: TelemetryRepository,
|
private telemetryRepository: TelemetryRepository,
|
||||||
|
private authService: AuthService,
|
||||||
) {
|
) {
|
||||||
logger.setAppName(this.worker);
|
logger.setAppName(this.worker);
|
||||||
}
|
}
|
||||||
@ -93,6 +95,14 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
|||||||
this.jobRepository.startWorkers();
|
this.jobRepository.startWorkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.eventRepository.setAuthFn(async (client) =>
|
||||||
|
this.authService.authenticate({
|
||||||
|
headers: client.request.headers,
|
||||||
|
queryParams: {},
|
||||||
|
metadata: { adminRoute: false, sharedLinkRoute: false, uri: '/api/socket.io' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
this.eventRepository.setup({ services });
|
this.eventRepository.setup({ services });
|
||||||
await this.eventRepository.emit('app.bootstrap');
|
await this.eventRepository.emit('app.bootstrap');
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ import { ApiExtension, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagge
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants';
|
import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants';
|
||||||
import { ImmichWorker, MetadataKey } from 'src/enum';
|
import { ImmichWorker, MetadataKey } from 'src/enum';
|
||||||
import { EmitEvent } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, QueueName } from 'src/interfaces/job.interface';
|
||||||
|
import { EmitEvent } from 'src/repositories/event.repository';
|
||||||
import { setUnion } from 'src/utils/set';
|
import { setUnion } from 'src/utils/set';
|
||||||
|
|
||||||
// PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the
|
// PostgreSQL uses a 16-bit integer to indicate the number of bound parameters. This means that the
|
||||||
|
@ -391,3 +391,10 @@ export enum DatabaseExtension {
|
|||||||
VECTOR = 'vector',
|
VECTOR = 'vector',
|
||||||
VECTORS = 'vectors',
|
VECTORS = 'vectors',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BootstrapEventPriority {
|
||||||
|
// Database service should be initialized before anything else, most other services need database access
|
||||||
|
DatabaseService = -200,
|
||||||
|
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
||||||
|
SystemConfig = 100,
|
||||||
|
}
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
import { ClassConstructor } from 'class-transformer';
|
|
||||||
import { SystemConfig } from 'src/config';
|
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
|
||||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
|
||||||
import { JobItem, QueueName } from 'src/interfaces/job.interface';
|
|
||||||
|
|
||||||
export const IEventRepository = 'IEventRepository';
|
|
||||||
|
|
||||||
type EventMap = {
|
|
||||||
// app events
|
|
||||||
'app.bootstrap': [];
|
|
||||||
'app.shutdown': [];
|
|
||||||
|
|
||||||
'config.init': [{ newConfig: SystemConfig }];
|
|
||||||
// config events
|
|
||||||
'config.update': [
|
|
||||||
{
|
|
||||||
newConfig: SystemConfig;
|
|
||||||
oldConfig: SystemConfig;
|
|
||||||
},
|
|
||||||
];
|
|
||||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
|
||||||
|
|
||||||
// album events
|
|
||||||
'album.update': [{ id: string; recipientIds: string[] }];
|
|
||||||
'album.invite': [{ id: string; userId: string }];
|
|
||||||
|
|
||||||
// asset events
|
|
||||||
'asset.tag': [{ assetId: string }];
|
|
||||||
'asset.untag': [{ assetId: string }];
|
|
||||||
'asset.hide': [{ assetId: string; userId: string }];
|
|
||||||
'asset.show': [{ assetId: string; userId: string }];
|
|
||||||
'asset.trash': [{ assetId: string; userId: string }];
|
|
||||||
'asset.delete': [{ assetId: string; userId: string }];
|
|
||||||
|
|
||||||
// asset bulk events
|
|
||||||
'assets.trash': [{ assetIds: string[]; userId: string }];
|
|
||||||
'assets.delete': [{ assetIds: string[]; userId: string }];
|
|
||||||
'assets.restore': [{ assetIds: string[]; userId: string }];
|
|
||||||
|
|
||||||
'job.start': [QueueName, JobItem];
|
|
||||||
|
|
||||||
// session events
|
|
||||||
'session.delete': [{ sessionId: string }];
|
|
||||||
|
|
||||||
// stack events
|
|
||||||
'stack.create': [{ stackId: string; userId: string }];
|
|
||||||
'stack.update': [{ stackId: string; userId: string }];
|
|
||||||
'stack.delete': [{ stackId: string; userId: string }];
|
|
||||||
|
|
||||||
// stack bulk events
|
|
||||||
'stacks.delete': [{ stackIds: string[]; userId: string }];
|
|
||||||
|
|
||||||
// user events
|
|
||||||
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
|
|
||||||
|
|
||||||
// websocket events
|
|
||||||
'websocket.connect': [{ userId: string }];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const serverEvents = ['config.update'] as const;
|
|
||||||
export type ServerEvents = (typeof serverEvents)[number];
|
|
||||||
|
|
||||||
export type EmitEvent = keyof EventMap;
|
|
||||||
export type EmitHandler<T extends EmitEvent> = (...args: ArgsOf<T>) => Promise<void> | void;
|
|
||||||
export type ArgOf<T extends EmitEvent> = EventMap[T][0];
|
|
||||||
export type ArgsOf<T extends EmitEvent> = EventMap[T];
|
|
||||||
|
|
||||||
export interface ClientEventMap {
|
|
||||||
on_upload_success: [AssetResponseDto];
|
|
||||||
on_user_delete: [string];
|
|
||||||
on_asset_delete: [string];
|
|
||||||
on_asset_trash: [string[]];
|
|
||||||
on_asset_update: [AssetResponseDto];
|
|
||||||
on_asset_hidden: [string];
|
|
||||||
on_asset_restore: [string[]];
|
|
||||||
on_asset_stack_update: string[];
|
|
||||||
on_person_thumbnail: [string];
|
|
||||||
on_server_version: [ServerVersionResponseDto];
|
|
||||||
on_config_update: [];
|
|
||||||
on_new_release: [ReleaseNotification];
|
|
||||||
on_session_delete: [string];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventItem<T extends EmitEvent> = {
|
|
||||||
event: T;
|
|
||||||
handler: EmitHandler<T>;
|
|
||||||
server: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum BootstrapEventPriority {
|
|
||||||
// Database service should be initialized before anything else, most other services need database access
|
|
||||||
DatabaseService = -200,
|
|
||||||
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
|
||||||
SystemConfig = 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IEventRepository {
|
|
||||||
setup(options: { services: ClassConstructor<unknown>[] }): void;
|
|
||||||
emit<T extends keyof EventMap>(event: T, ...args: ArgsOf<T>): Promise<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send to connected clients for a specific user
|
|
||||||
*/
|
|
||||||
clientSend<E extends keyof ClientEventMap>(event: E, room: string, ...data: ClientEventMap[E]): void;
|
|
||||||
/**
|
|
||||||
* Send to all connected clients
|
|
||||||
*/
|
|
||||||
clientBroadcast<E extends keyof ClientEventMap>(event: E, ...data: ClientEventMap[E]): void;
|
|
||||||
/**
|
|
||||||
* Send to all connected servers
|
|
||||||
*/
|
|
||||||
serverSend<T extends ServerEvents>(event: T, ...args: ArgsOf<T>): void;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
export const IMachineLearningRepository = 'IMachineLearningRepository';
|
|
||||||
|
|
||||||
export interface BoundingBox {
|
|
||||||
x1: number;
|
|
||||||
y1: number;
|
|
||||||
x2: number;
|
|
||||||
y2: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ModelTask {
|
|
||||||
FACIAL_RECOGNITION = 'facial-recognition',
|
|
||||||
SEARCH = 'clip',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ModelType {
|
|
||||||
DETECTION = 'detection',
|
|
||||||
PIPELINE = 'pipeline',
|
|
||||||
RECOGNITION = 'recognition',
|
|
||||||
TEXTUAL = 'textual',
|
|
||||||
VISUAL = 'visual',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ModelPayload = { imagePath: string } | { text: string };
|
|
||||||
|
|
||||||
type ModelOptions = { modelName: string };
|
|
||||||
|
|
||||||
export type FaceDetectionOptions = ModelOptions & { minScore: number };
|
|
||||||
|
|
||||||
type VisualResponse = { imageHeight: number; imageWidth: number };
|
|
||||||
export type ClipVisualRequest = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: ModelOptions } };
|
|
||||||
export type ClipVisualResponse = { [ModelTask.SEARCH]: string } & VisualResponse;
|
|
||||||
|
|
||||||
export type ClipTextualRequest = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: ModelOptions } };
|
|
||||||
export type ClipTextualResponse = { [ModelTask.SEARCH]: string };
|
|
||||||
|
|
||||||
export type FacialRecognitionRequest = {
|
|
||||||
[ModelTask.FACIAL_RECOGNITION]: {
|
|
||||||
[ModelType.DETECTION]: ModelOptions & { options: { minScore: number } };
|
|
||||||
[ModelType.RECOGNITION]: ModelOptions;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Face {
|
|
||||||
boundingBox: BoundingBox;
|
|
||||||
embedding: string;
|
|
||||||
score: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FacialRecognitionResponse = { [ModelTask.FACIAL_RECOGNITION]: Face[] } & VisualResponse;
|
|
||||||
export type DetectedFaces = { faces: Face[] } & VisualResponse;
|
|
||||||
export type MachineLearningRequest = ClipVisualRequest | ClipTextualRequest | FacialRecognitionRequest;
|
|
||||||
|
|
||||||
export interface IMachineLearningRepository {
|
|
||||||
encodeImage(urls: string[], imagePath: string, config: ModelOptions): Promise<string>;
|
|
||||||
encodeText(urls: string[], text: string, config: ModelOptions): Promise<string>;
|
|
||||||
detectFaces(urls: string[], imagePath: string, config: FaceDetectionOptions): Promise<DetectedFaces>;
|
|
||||||
}
|
|
@ -10,21 +10,15 @@ import {
|
|||||||
import { ClassConstructor } from 'class-transformer';
|
import { ClassConstructor } from 'class-transformer';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Server, Socket } from 'socket.io';
|
import { Server, Socket } from 'socket.io';
|
||||||
|
import { SystemConfig } from 'src/config';
|
||||||
import { EventConfig } from 'src/decorators';
|
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 { ImmichWorker, MetadataKey } from 'src/enum';
|
||||||
import {
|
import { JobItem, QueueName } from 'src/interfaces/job.interface';
|
||||||
ArgsOf,
|
|
||||||
ClientEventMap,
|
|
||||||
EmitEvent,
|
|
||||||
EmitHandler,
|
|
||||||
EventItem,
|
|
||||||
IEventRepository,
|
|
||||||
serverEvents,
|
|
||||||
ServerEvents,
|
|
||||||
} from 'src/interfaces/event.interface';
|
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { AuthService } from 'src/services/auth.service';
|
|
||||||
import { handlePromiseError } from 'src/utils/misc';
|
import { handlePromiseError } from 'src/utils/misc';
|
||||||
|
|
||||||
type EmitHandlers = Partial<{ [T in EmitEvent]: Array<EventItem<T>> }>;
|
type EmitHandlers = Partial<{ [T in EmitEvent]: Array<EventItem<T>> }>;
|
||||||
@ -37,14 +31,99 @@ type Item<T extends EmitEvent> = {
|
|||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type EventMap = {
|
||||||
|
// app events
|
||||||
|
'app.bootstrap': [];
|
||||||
|
'app.shutdown': [];
|
||||||
|
|
||||||
|
'config.init': [{ newConfig: SystemConfig }];
|
||||||
|
// config events
|
||||||
|
'config.update': [
|
||||||
|
{
|
||||||
|
newConfig: SystemConfig;
|
||||||
|
oldConfig: SystemConfig;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||||
|
|
||||||
|
// album events
|
||||||
|
'album.update': [{ id: string; recipientIds: string[] }];
|
||||||
|
'album.invite': [{ id: string; userId: string }];
|
||||||
|
|
||||||
|
// asset events
|
||||||
|
'asset.tag': [{ assetId: string }];
|
||||||
|
'asset.untag': [{ assetId: string }];
|
||||||
|
'asset.hide': [{ assetId: string; userId: string }];
|
||||||
|
'asset.show': [{ assetId: string; userId: string }];
|
||||||
|
'asset.trash': [{ assetId: string; userId: string }];
|
||||||
|
'asset.delete': [{ assetId: string; userId: string }];
|
||||||
|
|
||||||
|
// asset bulk events
|
||||||
|
'assets.trash': [{ assetIds: string[]; userId: string }];
|
||||||
|
'assets.delete': [{ assetIds: string[]; userId: string }];
|
||||||
|
'assets.restore': [{ assetIds: string[]; userId: string }];
|
||||||
|
|
||||||
|
'job.start': [QueueName, JobItem];
|
||||||
|
|
||||||
|
// session events
|
||||||
|
'session.delete': [{ sessionId: string }];
|
||||||
|
|
||||||
|
// stack events
|
||||||
|
'stack.create': [{ stackId: string; userId: string }];
|
||||||
|
'stack.update': [{ stackId: string; userId: string }];
|
||||||
|
'stack.delete': [{ stackId: string; userId: string }];
|
||||||
|
|
||||||
|
// stack bulk events
|
||||||
|
'stacks.delete': [{ stackIds: string[]; userId: string }];
|
||||||
|
|
||||||
|
// user events
|
||||||
|
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
|
||||||
|
|
||||||
|
// websocket events
|
||||||
|
'websocket.connect': [{ userId: string }];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serverEvents = ['config.update'] as const;
|
||||||
|
export type ServerEvents = (typeof serverEvents)[number];
|
||||||
|
|
||||||
|
export type EmitEvent = keyof EventMap;
|
||||||
|
export type EmitHandler<T extends EmitEvent> = (...args: ArgsOf<T>) => Promise<void> | void;
|
||||||
|
export type ArgOf<T extends EmitEvent> = EventMap[T][0];
|
||||||
|
export type ArgsOf<T extends EmitEvent> = EventMap[T];
|
||||||
|
|
||||||
|
export interface ClientEventMap {
|
||||||
|
on_upload_success: [AssetResponseDto];
|
||||||
|
on_user_delete: [string];
|
||||||
|
on_asset_delete: [string];
|
||||||
|
on_asset_trash: [string[]];
|
||||||
|
on_asset_update: [AssetResponseDto];
|
||||||
|
on_asset_hidden: [string];
|
||||||
|
on_asset_restore: [string[]];
|
||||||
|
on_asset_stack_update: string[];
|
||||||
|
on_person_thumbnail: [string];
|
||||||
|
on_server_version: [ServerVersionResponseDto];
|
||||||
|
on_config_update: [];
|
||||||
|
on_new_release: [ReleaseNotification];
|
||||||
|
on_session_delete: [string];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventItem<T extends EmitEvent> = {
|
||||||
|
event: T;
|
||||||
|
handler: EmitHandler<T>;
|
||||||
|
server: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AuthFn = (client: Socket) => Promise<AuthDto>;
|
||||||
|
|
||||||
@WebSocketGateway({
|
@WebSocketGateway({
|
||||||
cors: true,
|
cors: true,
|
||||||
path: '/api/socket.io',
|
path: '/api/socket.io',
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
})
|
})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, IEventRepository {
|
export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit {
|
||||||
private emitHandlers: EmitHandlers = {};
|
private emitHandlers: EmitHandlers = {};
|
||||||
|
private authFn?: AuthFn;
|
||||||
|
|
||||||
@WebSocketServer()
|
@WebSocketServer()
|
||||||
private server?: Server;
|
private server?: Server;
|
||||||
@ -122,11 +201,7 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
|||||||
async handleConnection(client: Socket) {
|
async handleConnection(client: Socket) {
|
||||||
try {
|
try {
|
||||||
this.logger.log(`Websocket Connect: ${client.id}`);
|
this.logger.log(`Websocket Connect: ${client.id}`);
|
||||||
const auth = await this.moduleRef.get(AuthService).authenticate({
|
const auth = await this.authenticate(client);
|
||||||
headers: client.request.headers,
|
|
||||||
queryParams: {},
|
|
||||||
metadata: { adminRoute: false, sharedLinkRoute: false, uri: '/api/socket.io' },
|
|
||||||
});
|
|
||||||
await client.join(auth.user.id);
|
await client.join(auth.user.id);
|
||||||
if (auth.session) {
|
if (auth.session) {
|
||||||
await client.join(auth.session.id);
|
await client.join(auth.session.id);
|
||||||
@ -182,4 +257,16 @@ export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect
|
|||||||
this.logger.debug(`Server event: ${event} (send)`);
|
this.logger.debug(`Server event: ${event} (send)`);
|
||||||
this.server?.serverSideEmit(event, ...args);
|
this.server?.serverSideEmit(event, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAuthFn(fn: (client: Socket) => Promise<AuthDto>) {
|
||||||
|
this.authFn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async authenticate(client: Socket) {
|
||||||
|
if (!this.authFn) {
|
||||||
|
throw new Error('Auth function not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.authFn(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||||
@ -53,8 +51,10 @@ export const repositories = [
|
|||||||
CronRepository,
|
CronRepository,
|
||||||
CryptoRepository,
|
CryptoRepository,
|
||||||
DatabaseRepository,
|
DatabaseRepository,
|
||||||
|
EventRepository,
|
||||||
LibraryRepository,
|
LibraryRepository,
|
||||||
LoggingRepository,
|
LoggingRepository,
|
||||||
|
MachineLearningRepository,
|
||||||
MapRepository,
|
MapRepository,
|
||||||
MediaRepository,
|
MediaRepository,
|
||||||
MemoryRepository,
|
MemoryRepository,
|
||||||
@ -80,8 +80,4 @@ export const repositories = [
|
|||||||
VersionHistoryRepository,
|
VersionHistoryRepository,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const providers = [
|
export const providers = [{ provide: IJobRepository, useClass: JobRepository }];
|
||||||
{ provide: IEventRepository, useClass: EventRepository },
|
|
||||||
{ provide: IJobRepository, useClass: JobRepository },
|
|
||||||
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
|
||||||
];
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { getQueueToken } from '@nestjs/bullmq';
|
import { getQueueToken } from '@nestjs/bullmq';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ModuleRef, Reflector } from '@nestjs/core';
|
import { ModuleRef, Reflector } from '@nestjs/core';
|
||||||
import { JobsOptions, Queue, Worker } from 'bullmq';
|
import { JobsOptions, Queue, Worker } from 'bullmq';
|
||||||
import { ClassConstructor } from 'class-transformer';
|
import { ClassConstructor } from 'class-transformer';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
import { JobConfig } from 'src/decorators';
|
import { JobConfig } from 'src/decorators';
|
||||||
import { MetadataKey } from 'src/enum';
|
import { MetadataKey } from 'src/enum';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import {
|
import {
|
||||||
IEntityJob,
|
IEntityJob,
|
||||||
IJobRepository,
|
IJobRepository,
|
||||||
@ -20,6 +19,7 @@ import {
|
|||||||
QueueStatus,
|
QueueStatus,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
|
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export class JobRepository implements IJobRepository {
|
|||||||
constructor(
|
constructor(
|
||||||
private moduleRef: ModuleRef,
|
private moduleRef: ModuleRef,
|
||||||
private configRepository: ConfigRepository,
|
private configRepository: ConfigRepository,
|
||||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
private eventRepository: EventRepository,
|
||||||
private logger: LoggingRepository,
|
private logger: LoggingRepository,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(JobRepository.name);
|
this.logger.setContext(JobRepository.name);
|
||||||
|
@ -1,21 +1,60 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { CLIPConfig } from 'src/dtos/model-config.dto';
|
import { CLIPConfig } from 'src/dtos/model-config.dto';
|
||||||
import {
|
|
||||||
ClipTextualResponse,
|
|
||||||
ClipVisualResponse,
|
|
||||||
FaceDetectionOptions,
|
|
||||||
FacialRecognitionResponse,
|
|
||||||
IMachineLearningRepository,
|
|
||||||
MachineLearningRequest,
|
|
||||||
ModelPayload,
|
|
||||||
ModelTask,
|
|
||||||
ModelType,
|
|
||||||
} from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
|
||||||
|
export interface BoundingBox {
|
||||||
|
x1: number;
|
||||||
|
y1: number;
|
||||||
|
x2: number;
|
||||||
|
y2: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ModelTask {
|
||||||
|
FACIAL_RECOGNITION = 'facial-recognition',
|
||||||
|
SEARCH = 'clip',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ModelType {
|
||||||
|
DETECTION = 'detection',
|
||||||
|
PIPELINE = 'pipeline',
|
||||||
|
RECOGNITION = 'recognition',
|
||||||
|
TEXTUAL = 'textual',
|
||||||
|
VISUAL = 'visual',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModelPayload = { imagePath: string } | { text: string };
|
||||||
|
|
||||||
|
type ModelOptions = { modelName: string };
|
||||||
|
|
||||||
|
export type FaceDetectionOptions = ModelOptions & { minScore: number };
|
||||||
|
|
||||||
|
type VisualResponse = { imageHeight: number; imageWidth: number };
|
||||||
|
export type ClipVisualRequest = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: ModelOptions } };
|
||||||
|
export type ClipVisualResponse = { [ModelTask.SEARCH]: string } & VisualResponse;
|
||||||
|
|
||||||
|
export type ClipTextualRequest = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: ModelOptions } };
|
||||||
|
export type ClipTextualResponse = { [ModelTask.SEARCH]: string };
|
||||||
|
|
||||||
|
export type FacialRecognitionRequest = {
|
||||||
|
[ModelTask.FACIAL_RECOGNITION]: {
|
||||||
|
[ModelType.DETECTION]: ModelOptions & { options: { minScore: number } };
|
||||||
|
[ModelType.RECOGNITION]: ModelOptions;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Face {
|
||||||
|
boundingBox: BoundingBox;
|
||||||
|
embedding: string;
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FacialRecognitionResponse = { [ModelTask.FACIAL_RECOGNITION]: Face[] } & VisualResponse;
|
||||||
|
export type DetectedFaces = { faces: Face[] } & VisualResponse;
|
||||||
|
export type MachineLearningRequest = ClipVisualRequest | ClipTextualRequest | FacialRecognitionRequest;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MachineLearningRepository implements IMachineLearningRepository {
|
export class MachineLearningRepository {
|
||||||
constructor(private logger: LoggingRepository) {
|
constructor(private logger: LoggingRepository) {
|
||||||
this.logger.setContext(MachineLearningRepository.name);
|
this.logger.setContext(MachineLearningRepository.name);
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import semver from 'semver';
|
|||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { ImmichWorker, StorageFolder } from 'src/enum';
|
import { ImmichWorker, StorageFolder } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { handlePromiseError } from 'src/utils/misc';
|
import { handlePromiseError } from 'src/utils/misc';
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ import { SALT_ROUNDS } from 'src/constants';
|
|||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { Users } from 'src/db';
|
import { Users } from 'src/db';
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||||
@ -20,8 +18,10 @@ import { ConfigRepository } from 'src/repositories/config.repository';
|
|||||||
import { CronRepository } from 'src/repositories/cron.repository';
|
import { CronRepository } from 'src/repositories/cron.repository';
|
||||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
||||||
import { DatabaseRepository } from 'src/repositories/database.repository';
|
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||||
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { LibraryRepository } from 'src/repositories/library.repository';
|
import { LibraryRepository } from 'src/repositories/library.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { MachineLearningRepository } from 'src/repositories/machine-learning.repository';
|
||||||
import { MapRepository } from 'src/repositories/map.repository';
|
import { MapRepository } from 'src/repositories/map.repository';
|
||||||
import { MediaRepository } from 'src/repositories/media.repository';
|
import { MediaRepository } from 'src/repositories/media.repository';
|
||||||
import { MemoryRepository } from 'src/repositories/memory.repository';
|
import { MemoryRepository } from 'src/repositories/memory.repository';
|
||||||
@ -63,11 +63,11 @@ export class BaseService {
|
|||||||
protected cronRepository: CronRepository,
|
protected cronRepository: CronRepository,
|
||||||
protected cryptoRepository: CryptoRepository,
|
protected cryptoRepository: CryptoRepository,
|
||||||
protected databaseRepository: DatabaseRepository,
|
protected databaseRepository: DatabaseRepository,
|
||||||
@Inject(IEventRepository) protected eventRepository: IEventRepository,
|
protected eventRepository: EventRepository,
|
||||||
@Inject(IJobRepository) protected jobRepository: IJobRepository,
|
@Inject(IJobRepository) protected jobRepository: IJobRepository,
|
||||||
protected keyRepository: ApiKeyRepository,
|
protected keyRepository: ApiKeyRepository,
|
||||||
protected libraryRepository: LibraryRepository,
|
protected libraryRepository: LibraryRepository,
|
||||||
@Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository,
|
protected machineLearningRepository: MachineLearningRepository,
|
||||||
protected mapRepository: MapRepository,
|
protected mapRepository: MapRepository,
|
||||||
protected mediaRepository: MediaRepository,
|
protected mediaRepository: MediaRepository,
|
||||||
protected memoryRepository: MemoryRepository,
|
protected memoryRepository: MemoryRepository,
|
||||||
|
@ -2,8 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { Duration } from 'luxon';
|
import { Duration } from 'luxon';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import { OnEvent } from 'src/decorators';
|
import { OnEvent } from 'src/decorators';
|
||||||
import { DatabaseExtension } from 'src/enum';
|
import { BootstrapEventPriority, DatabaseExtension } from 'src/enum';
|
||||||
import { BootstrapEventPriority } from 'src/interfaces/event.interface';
|
|
||||||
import { DatabaseLock, EXTENSION_NAMES, VectorExtension, VectorIndex } from 'src/repositories/database.repository';
|
import { DatabaseLock, EXTENSION_NAMES, VectorExtension, VectorIndex } from 'src/repositories/database.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import { OnEvent } from 'src/decorators';
|
|||||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto';
|
import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobStatusDto } from 'src/dtos/job.dto';
|
||||||
import { AssetType, ImmichWorker, ManualJobName } from 'src/enum';
|
import { AssetType, ImmichWorker, ManualJobName } from 'src/enum';
|
||||||
import { ArgOf, ArgsOf } from 'src/interfaces/event.interface';
|
|
||||||
import {
|
import {
|
||||||
ConcurrentQueueName,
|
ConcurrentQueueName,
|
||||||
JobCommand,
|
JobCommand,
|
||||||
@ -14,6 +13,7 @@ import {
|
|||||||
QueueCleanType,
|
QueueCleanType,
|
||||||
QueueName,
|
QueueName,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
|
import { ArgOf, ArgsOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
|
||||||
const asJobItem = (dto: JobCreateDto): JobItem => {
|
const asJobItem = (dto: JobCreateDto): JobItem => {
|
||||||
|
@ -17,9 +17,9 @@ import {
|
|||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
import { AssetType, ImmichWorker } from 'src/enum';
|
import { AssetType, ImmichWorker } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, JobOf, JOBS_LIBRARY_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobOf, JOBS_LIBRARY_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
import { handlePromiseError } from 'src/utils/misc';
|
import { handlePromiseError } from 'src/utils/misc';
|
||||||
|
@ -14,10 +14,10 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
|||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
import { PersonEntity } from 'src/entities/person.entity';
|
||||||
import { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
|
import { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { WithoutProperty } from 'src/repositories/asset.repository';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { ReverseGeocodeResult } from 'src/repositories/map.repository';
|
import { ReverseGeocodeResult } from 'src/repositories/map.repository';
|
||||||
import { ImmichTags } from 'src/repositories/metadata.repository';
|
import { ImmichTags } from 'src/repositories/metadata.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
@ -2,7 +2,6 @@ import { BadRequestException, Injectable } from '@nestjs/common';
|
|||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
import { AlbumEntity } from 'src/entities/album.entity';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import {
|
import {
|
||||||
IEntityJob,
|
IEntityJob,
|
||||||
INotifyAlbumUpdateJob,
|
INotifyAlbumUpdateJob,
|
||||||
@ -12,6 +11,7 @@ import {
|
|||||||
JobStatus,
|
JobStatus,
|
||||||
QueueName,
|
QueueName,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { EmailImageAttachment, EmailTemplate } from 'src/repositories/notification.repository';
|
import { EmailImageAttachment, EmailTemplate } from 'src/repositories/notification.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
|
@ -4,8 +4,8 @@ import { mapFaces, mapPerson, PersonResponseDto } from 'src/dtos/person.dto';
|
|||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||||
import { CacheControl, Colorspace, ImageFormat, SourceType, SystemMetadataKey } from 'src/enum';
|
import { CacheControl, Colorspace, ImageFormat, SourceType, SystemMetadataKey } from 'src/enum';
|
||||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
import { DetectedFaces } from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { WithoutProperty } from 'src/repositories/asset.repository';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
|
import { DetectedFaces } from 'src/repositories/machine-learning.repository';
|
||||||
import { FaceSearchResult } from 'src/repositories/search.repository';
|
import { FaceSearchResult } from 'src/repositories/search.repository';
|
||||||
import { PersonService } from 'src/services/person.service';
|
import { PersonService } from 'src/services/person.service';
|
||||||
import { ImmichFileResponse } from 'src/utils/file';
|
import { ImmichFileResponse } from 'src/utils/file';
|
||||||
|
@ -40,8 +40,8 @@ import {
|
|||||||
JobStatus,
|
JobStatus,
|
||||||
QueueName,
|
QueueName,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
import { BoundingBox } from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { WithoutProperty } from 'src/repositories/asset.repository';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
|
import { BoundingBox } from 'src/repositories/machine-learning.repository';
|
||||||
import { UpdateFacesData } from 'src/repositories/person.repository';
|
import { UpdateFacesData } from 'src/repositories/person.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { CropOptions, ImageDimensions, InputDimensions } from 'src/types';
|
import { CropOptions, ImageDimensions, InputDimensions } from 'src/types';
|
||||||
|
@ -2,10 +2,10 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { SystemConfig } from 'src/config';
|
import { SystemConfig } from 'src/config';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { ImmichWorker } from 'src/enum';
|
import { ImmichWorker } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JOBS_ASSET_PAGINATION_SIZE, JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { WithoutProperty } from 'src/repositories/asset.repository';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
|
import { getCLIPModelInfo, isSmartSearchEnabled } from 'src/utils/misc';
|
||||||
|
@ -8,9 +8,9 @@ import { OnEvent, OnJob } from 'src/decorators';
|
|||||||
import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { AssetPathType, AssetType, StorageFolder } from 'src/enum';
|
import { AssetPathType, AssetType, StorageFolder } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobOf, JOBS_ASSET_PAGINATION_SIZE, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getLivePhotoMotionFilename } from 'src/utils/file';
|
import { getLivePhotoMotionFilename } from 'src/utils/file';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
@ -4,7 +4,8 @@ import _ from 'lodash';
|
|||||||
import { defaults } from 'src/config';
|
import { defaults } from 'src/config';
|
||||||
import { OnEvent } from 'src/decorators';
|
import { OnEvent } from 'src/decorators';
|
||||||
import { SystemConfigDto, mapConfig } from 'src/dtos/system-config.dto';
|
import { SystemConfigDto, mapConfig } from 'src/dtos/system-config.dto';
|
||||||
import { ArgOf, BootstrapEventPriority } from 'src/interfaces/event.interface';
|
import { BootstrapEventPriority } from 'src/enum';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { clearConfigCache } from 'src/utils/config';
|
import { clearConfigCache } from 'src/utils/config';
|
||||||
import { toPlainObject } from 'src/utils/object';
|
import { toPlainObject } from 'src/utils/object';
|
||||||
|
@ -6,9 +6,9 @@ import { OnEvent, OnJob } from 'src/decorators';
|
|||||||
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto';
|
||||||
import { VersionCheckMetadata } from 'src/entities/system-metadata.entity';
|
import { VersionCheckMetadata } from 'src/entities/system-metadata.entity';
|
||||||
import { ImmichEnvironment, SystemMetadataKey } from 'src/enum';
|
import { ImmichEnvironment, SystemMetadataKey } from 'src/enum';
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
|
||||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { DatabaseLock } from 'src/repositories/database.repository';
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
|
import { ArgOf } from 'src/repositories/event.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
|
||||||
const asNotification = ({ checkedAt, releaseVersion }: VersionCheckMetadata): ReleaseNotification => {
|
const asNotification = ({ checkedAt, releaseVersion }: VersionCheckMetadata): ReleaseNotification => {
|
||||||
|
@ -5,11 +5,11 @@ import { UploadFieldName } from 'src/dtos/asset-media.dto';
|
|||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
||||||
import { AssetFileType, AssetType, Permission } from 'src/enum';
|
import { AssetFileType, AssetType, Permission } from 'src/enum';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import { AuthRequest } from 'src/middleware/auth.guard';
|
import { AuthRequest } from 'src/middleware/auth.guard';
|
||||||
import { ImmichFile } from 'src/middleware/file-upload.interceptor';
|
import { ImmichFile } from 'src/middleware/file-upload.interceptor';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { PartnerRepository } from 'src/repositories/partner.repository';
|
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||||
import { UploadFile } from 'src/services/asset-media.service';
|
import { UploadFile } from 'src/services/asset-media.service';
|
||||||
import { checkAccess } from 'src/utils/access';
|
import { checkAccess } from 'src/utils/access';
|
||||||
@ -139,7 +139,7 @@ export const getMyPartnerIds = async ({ userId, repository, timelineEnabled }: P
|
|||||||
return [...partnerIds];
|
return [...partnerIds];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AssetHookRepositories = { asset: AssetRepository; event: IEventRepository };
|
export type AssetHookRepositories = { asset: AssetRepository; event: EventRepository };
|
||||||
|
|
||||||
export const onBeforeLink = async (
|
export const onBeforeLink = async (
|
||||||
{ asset: assetRepository, event: eventRepository }: AssetHookRepositories,
|
{ asset: assetRepository, event: eventRepository }: AssetHookRepositories,
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newEventRepositoryMock = (): Mocked<IEventRepository> => {
|
export const newEventRepositoryMock = (): Mocked<RepositoryInterface<EventRepository>> => {
|
||||||
return {
|
return {
|
||||||
setup: vitest.fn(),
|
setup: vitest.fn(),
|
||||||
emit: vitest.fn() as any,
|
emit: vitest.fn() as any,
|
||||||
clientSend: vitest.fn() as any,
|
clientSend: vitest.fn() as any,
|
||||||
clientBroadcast: vitest.fn() as any,
|
clientBroadcast: vitest.fn() as any,
|
||||||
serverSend: vitest.fn(),
|
serverSend: vitest.fn(),
|
||||||
|
afterInit: vitest.fn(),
|
||||||
|
handleConnection: vitest.fn(),
|
||||||
|
handleDisconnect: vitest.fn(),
|
||||||
|
setAuthFn: vitest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { MachineLearningRepository } from 'src/repositories/machine-learning.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newMachineLearningRepositoryMock = (): Mocked<IMachineLearningRepository> => {
|
export const newMachineLearningRepositoryMock = (): Mocked<RepositoryInterface<MachineLearningRepository>> => {
|
||||||
return {
|
return {
|
||||||
encodeImage: vitest.fn(),
|
encodeImage: vitest.fn(),
|
||||||
encodeText: vitest.fn(),
|
encodeText: vitest.fn(),
|
||||||
|
@ -2,8 +2,6 @@ import { ChildProcessWithoutNullStreams } from 'node:child_process';
|
|||||||
import { Writable } from 'node:stream';
|
import { Writable } from 'node:stream';
|
||||||
import { PNG } from 'pngjs';
|
import { PNG } from 'pngjs';
|
||||||
import { ImmichWorker } from 'src/enum';
|
import { ImmichWorker } from 'src/enum';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||||
@ -15,9 +13,11 @@ import { ConfigRepository } from 'src/repositories/config.repository';
|
|||||||
import { CronRepository } from 'src/repositories/cron.repository';
|
import { CronRepository } from 'src/repositories/cron.repository';
|
||||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
||||||
import { DatabaseRepository } from 'src/repositories/database.repository';
|
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||||
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { JobRepository } from 'src/repositories/job.repository';
|
import { JobRepository } from 'src/repositories/job.repository';
|
||||||
import { LibraryRepository } from 'src/repositories/library.repository';
|
import { LibraryRepository } from 'src/repositories/library.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { MachineLearningRepository } from 'src/repositories/machine-learning.repository';
|
||||||
import { MapRepository } from 'src/repositories/map.repository';
|
import { MapRepository } from 'src/repositories/map.repository';
|
||||||
import { MediaRepository } from 'src/repositories/media.repository';
|
import { MediaRepository } from 'src/repositories/media.repository';
|
||||||
import { MemoryRepository } from 'src/repositories/memory.repository';
|
import { MemoryRepository } from 'src/repositories/memory.repository';
|
||||||
@ -108,11 +108,11 @@ export type ServiceMocks = {
|
|||||||
cron: Mocked<RepositoryInterface<CronRepository>>;
|
cron: Mocked<RepositoryInterface<CronRepository>>;
|
||||||
crypto: Mocked<RepositoryInterface<CryptoRepository>>;
|
crypto: Mocked<RepositoryInterface<CryptoRepository>>;
|
||||||
database: Mocked<RepositoryInterface<DatabaseRepository>>;
|
database: Mocked<RepositoryInterface<DatabaseRepository>>;
|
||||||
event: Mocked<IEventRepository>;
|
event: Mocked<RepositoryInterface<EventRepository>>;
|
||||||
job: Mocked<RepositoryInterface<JobRepository>>;
|
job: Mocked<RepositoryInterface<JobRepository>>;
|
||||||
library: Mocked<RepositoryInterface<LibraryRepository>>;
|
library: Mocked<RepositoryInterface<LibraryRepository>>;
|
||||||
logger: Mocked<ILoggingRepository>;
|
logger: Mocked<ILoggingRepository>;
|
||||||
machineLearning: Mocked<IMachineLearningRepository>;
|
machineLearning: Mocked<RepositoryInterface<MachineLearningRepository>>;
|
||||||
map: Mocked<RepositoryInterface<MapRepository>>;
|
map: Mocked<RepositoryInterface<MapRepository>>;
|
||||||
media: Mocked<RepositoryInterface<MediaRepository>>;
|
media: Mocked<RepositoryInterface<MediaRepository>>;
|
||||||
memory: Mocked<RepositoryInterface<MemoryRepository>>;
|
memory: Mocked<RepositoryInterface<MemoryRepository>>;
|
||||||
@ -198,11 +198,11 @@ export const newTestService = <T extends BaseService>(
|
|||||||
cronMock as RepositoryInterface<CronRepository> as CronRepository,
|
cronMock as RepositoryInterface<CronRepository> as CronRepository,
|
||||||
cryptoMock as RepositoryInterface<CryptoRepository> as CryptoRepository,
|
cryptoMock as RepositoryInterface<CryptoRepository> as CryptoRepository,
|
||||||
databaseMock as RepositoryInterface<DatabaseRepository> as DatabaseRepository,
|
databaseMock as RepositoryInterface<DatabaseRepository> as DatabaseRepository,
|
||||||
eventMock,
|
eventMock as RepositoryInterface<EventRepository> as EventRepository,
|
||||||
jobMock,
|
jobMock,
|
||||||
apiKeyMock as RepositoryInterface<ApiKeyRepository> as ApiKeyRepository,
|
apiKeyMock as RepositoryInterface<ApiKeyRepository> as ApiKeyRepository,
|
||||||
libraryMock as RepositoryInterface<LibraryRepository> as LibraryRepository,
|
libraryMock as RepositoryInterface<LibraryRepository> as LibraryRepository,
|
||||||
machineLearningMock,
|
machineLearningMock as RepositoryInterface<MachineLearningRepository> as MachineLearningRepository,
|
||||||
mapMock as RepositoryInterface<MapRepository> as MapRepository,
|
mapMock as RepositoryInterface<MapRepository> as MapRepository,
|
||||||
mediaMock as RepositoryInterface<MediaRepository> as MediaRepository,
|
mediaMock as RepositoryInterface<MediaRepository> as MediaRepository,
|
||||||
memoryMock as RepositoryInterface<MemoryRepository> as MemoryRepository,
|
memoryMock as RepositoryInterface<MemoryRepository> as MemoryRepository,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user