mirror of
https://github.com/immich-app/immich.git
synced 2025-05-30 19:54:52 -04:00
refactor: repositories (#16036)
This commit is contained in:
parent
d2575d8f00
commit
9d85272c2b
@ -5,6 +5,7 @@ vitest.mock('src/constants', () => ({
|
|||||||
APP_MEDIA_LOCATION: '/photos',
|
APP_MEDIA_LOCATION: '/photos',
|
||||||
ADDED_IN_PREFIX: 'This property was added in ',
|
ADDED_IN_PREFIX: 'This property was added in ',
|
||||||
DEPRECATED_IN_PREFIX: 'This property was deprecated in ',
|
DEPRECATED_IN_PREFIX: 'This property was deprecated in ',
|
||||||
|
IWorker: 'IWorker',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('StorageCore', () => {
|
describe('StorageCore', () => {
|
||||||
|
@ -4,13 +4,13 @@ import { APP_MEDIA_LOCATION } from 'src/constants';
|
|||||||
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 { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum';
|
import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum';
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
|
||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { MoveRepository } from 'src/repositories/move.repository';
|
||||||
|
import { PersonRepository } from 'src/repositories/person.repository';
|
||||||
|
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { getConfig } from 'src/utils/config';
|
import { getConfig } from 'src/utils/config';
|
||||||
@ -33,23 +33,23 @@ let instance: StorageCore | null;
|
|||||||
|
|
||||||
export class StorageCore {
|
export class StorageCore {
|
||||||
private constructor(
|
private constructor(
|
||||||
private assetRepository: IAssetRepository,
|
private assetRepository: AssetRepository,
|
||||||
private configRepository: ConfigRepository,
|
private configRepository: ConfigRepository,
|
||||||
private cryptoRepository: CryptoRepository,
|
private cryptoRepository: CryptoRepository,
|
||||||
private moveRepository: IMoveRepository,
|
private moveRepository: MoveRepository,
|
||||||
private personRepository: IPersonRepository,
|
private personRepository: PersonRepository,
|
||||||
private storageRepository: IStorageRepository,
|
private storageRepository: StorageRepository,
|
||||||
private systemMetadataRepository: SystemMetadataRepository,
|
private systemMetadataRepository: SystemMetadataRepository,
|
||||||
private logger: LoggingRepository,
|
private logger: LoggingRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
assetRepository: IAssetRepository,
|
assetRepository: AssetRepository,
|
||||||
configRepository: ConfigRepository,
|
configRepository: ConfigRepository,
|
||||||
cryptoRepository: CryptoRepository,
|
cryptoRepository: CryptoRepository,
|
||||||
moveRepository: IMoveRepository,
|
moveRepository: MoveRepository,
|
||||||
personRepository: IPersonRepository,
|
personRepository: PersonRepository,
|
||||||
storageRepository: IStorageRepository,
|
storageRepository: StorageRepository,
|
||||||
systemMetadataRepository: SystemMetadataRepository,
|
systemMetadataRepository: SystemMetadataRepository,
|
||||||
logger: LoggingRepository,
|
logger: LoggingRepository,
|
||||||
) {
|
) {
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AssetType } from 'src/enum';
|
import { AssetType } from 'src/enum';
|
||||||
import { AssetStats } from 'src/interfaces/asset.interface';
|
import { AssetStats } from 'src/repositories/asset.repository';
|
||||||
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class DeviceIdDto {
|
export class DeviceIdDto {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty } from 'class-validator';
|
import { IsEnum, IsNotEmpty } from 'class-validator';
|
||||||
import { UserResponseDto } from 'src/dtos/user.dto';
|
import { UserResponseDto } from 'src/dtos/user.dto';
|
||||||
import { PartnerDirection } from 'src/interfaces/partner.interface';
|
import { PartnerDirection } from 'src/repositories/partner.repository';
|
||||||
|
|
||||||
export class UpdatePartnerDto {
|
export class UpdatePartnerDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||||
import { AssetOrder } from 'src/enum';
|
import { AssetOrder } from 'src/enum';
|
||||||
import { TimeBucketSize } from 'src/interfaces/asset.interface';
|
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||||
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateBoolean, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class TimeBucketDto {
|
export class TimeBucketDto {
|
||||||
|
@ -13,8 +13,8 @@ import { StackEntity } from 'src/entities/stack.entity';
|
|||||||
import { TagEntity } from 'src/entities/tag.entity';
|
import { TagEntity } from 'src/entities/tag.entity';
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
||||||
import { TimeBucketSize } from 'src/interfaces/asset.interface';
|
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||||
import { AssetSearchBuilderOptions } from 'src/interfaces/search.interface';
|
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
||||||
import { anyUuid, asUuid } from 'src/utils/database';
|
import { anyUuid, asUuid } from 'src/utils/database';
|
||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
|
@ -384,3 +384,10 @@ export enum ExifOrientation {
|
|||||||
MirrorHorizontalRotate90CW = 7,
|
MirrorHorizontalRotate90CW = 7,
|
||||||
Rotate270CW = 8,
|
Rotate270CW = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DatabaseExtension {
|
||||||
|
CUBE = 'cube',
|
||||||
|
EARTH_DISTANCE = 'earthdistance',
|
||||||
|
VECTOR = 'vector',
|
||||||
|
VECTORS = 'vectors',
|
||||||
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { Albums } from 'src/db';
|
|
||||||
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
|
||||||
import { IBulkAsset } from 'src/utils/asset.util';
|
|
||||||
|
|
||||||
export const IAlbumRepository = 'IAlbumRepository';
|
|
||||||
|
|
||||||
export interface AlbumAssetCount {
|
|
||||||
albumId: string;
|
|
||||||
assetCount: number;
|
|
||||||
startDate: Date | null;
|
|
||||||
endDate: Date | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AlbumInfoOptions {
|
|
||||||
withAssets: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAlbumRepository extends IBulkAsset {
|
|
||||||
getById(id: string, options: AlbumInfoOptions): Promise<AlbumEntity | undefined>;
|
|
||||||
getByAssetId(ownerId: string, assetId: string): Promise<AlbumEntity[]>;
|
|
||||||
removeAsset(assetId: string): Promise<void>;
|
|
||||||
getMetadataForIds(ids: string[]): Promise<AlbumAssetCount[]>;
|
|
||||||
getOwned(ownerId: string): Promise<AlbumEntity[]>;
|
|
||||||
getShared(ownerId: string): Promise<AlbumEntity[]>;
|
|
||||||
getNotShared(ownerId: string): Promise<AlbumEntity[]>;
|
|
||||||
restoreAll(userId: string): Promise<void>;
|
|
||||||
softDeleteAll(userId: string): Promise<void>;
|
|
||||||
deleteAll(userId: string): Promise<void>;
|
|
||||||
create(album: Insertable<Albums>, assetIds: string[], albumUsers: AlbumUserCreateDto[]): Promise<AlbumEntity>;
|
|
||||||
update(id: string, album: Updateable<Albums>): Promise<AlbumEntity>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
updateThumbnails(): Promise<number | undefined>;
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { AssetFiles, AssetJobStatus, Assets, Exif } from 'src/db';
|
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
|
||||||
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
|
||||||
import { AssetSearchOptions, SearchExploreItem } from 'src/interfaces/search.interface';
|
|
||||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
|
||||||
|
|
||||||
export type AssetStats = Record<AssetType, number>;
|
|
||||||
|
|
||||||
export interface AssetStatsOptions {
|
|
||||||
isFavorite?: boolean;
|
|
||||||
isArchived?: boolean;
|
|
||||||
isTrashed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LivePhotoSearchOptions {
|
|
||||||
ownerId: string;
|
|
||||||
libraryId?: string | null;
|
|
||||||
livePhotoCID: string;
|
|
||||||
otherAssetId: string;
|
|
||||||
type: AssetType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WithoutProperty {
|
|
||||||
THUMBNAIL = 'thumbnail',
|
|
||||||
ENCODED_VIDEO = 'encoded-video',
|
|
||||||
EXIF = 'exif',
|
|
||||||
SMART_SEARCH = 'smart-search',
|
|
||||||
DUPLICATE = 'duplicate',
|
|
||||||
FACES = 'faces',
|
|
||||||
SIDECAR = 'sidecar',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WithProperty {
|
|
||||||
SIDECAR = 'sidecar',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum TimeBucketSize {
|
|
||||||
DAY = 'DAY',
|
|
||||||
MONTH = 'MONTH',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetBuilderOptions {
|
|
||||||
isArchived?: boolean;
|
|
||||||
isFavorite?: boolean;
|
|
||||||
isTrashed?: boolean;
|
|
||||||
isDuplicate?: boolean;
|
|
||||||
albumId?: string;
|
|
||||||
tagId?: string;
|
|
||||||
personId?: string;
|
|
||||||
userIds?: string[];
|
|
||||||
withStacked?: boolean;
|
|
||||||
exifInfo?: boolean;
|
|
||||||
status?: AssetStatus;
|
|
||||||
assetType?: AssetType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TimeBucketOptions extends AssetBuilderOptions {
|
|
||||||
size: TimeBucketSize;
|
|
||||||
order?: AssetOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TimeBucketItem {
|
|
||||||
timeBucket: string;
|
|
||||||
count: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MonthDay {
|
|
||||||
day: number;
|
|
||||||
month: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetExploreFieldOptions {
|
|
||||||
maxFields: number;
|
|
||||||
minAssetsPerField: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetFullSyncOptions {
|
|
||||||
ownerId: string;
|
|
||||||
lastId?: string;
|
|
||||||
updatedUntil: Date;
|
|
||||||
limit: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetDeltaSyncOptions {
|
|
||||||
userIds: string[];
|
|
||||||
updatedAfter: Date;
|
|
||||||
limit: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetUpdateDuplicateOptions {
|
|
||||||
targetDuplicateId: string | null;
|
|
||||||
assetIds: string[];
|
|
||||||
duplicateIds: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpsertFileOptions {
|
|
||||||
assetId: string;
|
|
||||||
type: AssetFileType;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetGetByChecksumOptions {
|
|
||||||
ownerId: string;
|
|
||||||
checksum: Buffer;
|
|
||||||
libraryId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>;
|
|
||||||
|
|
||||||
export interface GetByIdsRelations {
|
|
||||||
exifInfo?: boolean;
|
|
||||||
faces?: { person?: boolean };
|
|
||||||
files?: boolean;
|
|
||||||
library?: boolean;
|
|
||||||
owner?: boolean;
|
|
||||||
smartSearch?: boolean;
|
|
||||||
stack?: { assets?: boolean };
|
|
||||||
tags?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DuplicateGroup {
|
|
||||||
duplicateId: string;
|
|
||||||
assets: AssetEntity[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DayOfYearAssets {
|
|
||||||
yearsAgo: number;
|
|
||||||
assets: AssetEntity[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IAssetRepository = 'IAssetRepository';
|
|
||||||
|
|
||||||
export interface IAssetRepository {
|
|
||||||
create(asset: Insertable<Assets>): Promise<AssetEntity>;
|
|
||||||
getByIds(ids: string[], relations?: GetByIdsRelations): Promise<AssetEntity[]>;
|
|
||||||
getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]>;
|
|
||||||
getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise<DayOfYearAssets[]>;
|
|
||||||
getByChecksum(options: AssetGetByChecksumOptions): Promise<AssetEntity | undefined>;
|
|
||||||
getByChecksums(userId: string, checksums: Buffer[]): Promise<AssetEntity[]>;
|
|
||||||
getUploadAssetIdByChecksum(ownerId: string, checksum: Buffer): Promise<string | undefined>;
|
|
||||||
getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>;
|
|
||||||
getByDeviceIds(ownerId: string, deviceId: string, deviceAssetIds: string[]): Promise<string[]>;
|
|
||||||
getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
|
||||||
getById(id: string, relations?: GetByIdsRelations): Promise<AssetEntity | undefined>;
|
|
||||||
getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>;
|
|
||||||
getRandom(userIds: string[], count: number): Promise<AssetEntity[]>;
|
|
||||||
getLastUpdatedAssetForAlbumId(albumId: string): Promise<AssetEntity | undefined>;
|
|
||||||
getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise<AssetEntity | undefined>;
|
|
||||||
deleteAll(ownerId: string): Promise<void>;
|
|
||||||
getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>;
|
|
||||||
getAllByDeviceId(userId: string, deviceId: string): Promise<string[]>;
|
|
||||||
getLivePhotoCount(motionId: string): Promise<number>;
|
|
||||||
updateAll(ids: string[], options: Updateable<Assets>): Promise<void>;
|
|
||||||
updateDuplicates(options: AssetUpdateDuplicateOptions): Promise<void>;
|
|
||||||
update(asset: Updateable<Assets> & { id: string }): Promise<AssetEntity>;
|
|
||||||
remove(asset: AssetEntity): Promise<void>;
|
|
||||||
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | undefined>;
|
|
||||||
getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>;
|
|
||||||
getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>;
|
|
||||||
getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
|
|
||||||
upsertExif(exif: Insertable<Exif>): Promise<void>;
|
|
||||||
upsertJobStatus(...jobStatus: Insertable<AssetJobStatus>[]): Promise<void>;
|
|
||||||
getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise<SearchExploreItem<string>>;
|
|
||||||
getDuplicates(userId: string): Promise<DuplicateGroup[]>;
|
|
||||||
getAllForUserFullSync(options: AssetFullSyncOptions): Promise<AssetEntity[]>;
|
|
||||||
getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise<AssetEntity[]>;
|
|
||||||
upsertFile(options: Insertable<AssetFiles>): Promise<void>;
|
|
||||||
upsertFiles(options: Insertable<AssetFiles>[]): Promise<void>;
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
export const ICryptoRepository = 'ICryptoRepository';
|
|
||||||
|
|
||||||
export interface ICryptoRepository {
|
|
||||||
randomBytes(size: number): Buffer;
|
|
||||||
randomUUID(): string;
|
|
||||||
hashFile(filePath: string | Buffer): Promise<Buffer>;
|
|
||||||
hashSha256(data: string): string;
|
|
||||||
verifySha256(data: string, encrypted: string, publicKey: string): boolean;
|
|
||||||
hashSha1(data: string | Buffer): Buffer;
|
|
||||||
hashBcrypt(data: string | Buffer, saltOrRounds: string | number): Promise<string>;
|
|
||||||
compareBcrypt(data: string | Buffer, encrypted: string): boolean;
|
|
||||||
newPassword(bytes: number): string;
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
export enum DatabaseExtension {
|
|
||||||
CUBE = 'cube',
|
|
||||||
EARTH_DISTANCE = 'earthdistance',
|
|
||||||
VECTOR = 'vector',
|
|
||||||
VECTORS = 'vectors',
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IDatabaseRepository = 'IDatabaseRepository';
|
|
||||||
|
|
||||||
export interface IDatabaseRepository {
|
|
||||||
init(): void;
|
|
||||||
reconnect(): Promise<boolean>;
|
|
||||||
shutdown(): Promise<void>;
|
|
||||||
getExtensionVersion(extension: DatabaseExtension): Promise<ExtensionVersion>;
|
|
||||||
getExtensionVersionRange(extension: VectorExtension): string;
|
|
||||||
getPostgresVersion(): Promise<string>;
|
|
||||||
getPostgresVersionRange(): string;
|
|
||||||
createExtension(extension: DatabaseExtension): Promise<void>;
|
|
||||||
updateVectorExtension(extension: VectorExtension, version?: string): Promise<VectorUpdateResult>;
|
|
||||||
reindex(index: VectorIndex): Promise<void>;
|
|
||||||
shouldReindex(name: VectorIndex): Promise<boolean>;
|
|
||||||
runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise<void>;
|
|
||||||
withLock<R>(lock: DatabaseLock, callback: () => Promise<R>): Promise<R>;
|
|
||||||
tryLock(lock: DatabaseLock): Promise<boolean>;
|
|
||||||
isBusy(lock: DatabaseLock): boolean;
|
|
||||||
wait(lock: DatabaseLock): Promise<void>;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { Libraries } from 'src/db';
|
|
||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
|
||||||
|
|
||||||
export const ILibraryRepository = 'ILibraryRepository';
|
|
||||||
|
|
||||||
export interface ILibraryRepository {
|
|
||||||
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
|
||||||
getAllDeleted(): Promise<LibraryEntity[]>;
|
|
||||||
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | undefined>;
|
|
||||||
create(library: Insertable<Libraries>): Promise<LibraryEntity>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
softDelete(id: string): Promise<void>;
|
|
||||||
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity>;
|
|
||||||
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { MoveHistory } from 'src/db';
|
|
||||||
import { MoveEntity } from 'src/entities/move.entity';
|
|
||||||
import { PathType } from 'src/enum';
|
|
||||||
|
|
||||||
export const IMoveRepository = 'IMoveRepository';
|
|
||||||
|
|
||||||
export type MoveCreate = Pick<MoveEntity, 'oldPath' | 'newPath' | 'entityId' | 'pathType'> & Partial<MoveEntity>;
|
|
||||||
|
|
||||||
export interface IMoveRepository {
|
|
||||||
create(entity: Insertable<MoveHistory>): Promise<MoveEntity>;
|
|
||||||
getByEntity(entityId: string, pathType: PathType): Promise<MoveEntity | undefined>;
|
|
||||||
update(id: string, entity: Updateable<MoveHistory>): Promise<MoveEntity>;
|
|
||||||
delete(id: string): Promise<MoveEntity>;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import { Updateable } from 'kysely';
|
|
||||||
import { Partners } from 'src/db';
|
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
|
||||||
|
|
||||||
export interface PartnerIds {
|
|
||||||
sharedById: string;
|
|
||||||
sharedWithId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PartnerDirection {
|
|
||||||
SharedBy = 'shared-by',
|
|
||||||
SharedWith = 'shared-with',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IPartnerRepository = 'IPartnerRepository';
|
|
||||||
|
|
||||||
export interface IPartnerRepository {
|
|
||||||
getAll(userId: string): Promise<PartnerEntity[]>;
|
|
||||||
get(partner: PartnerIds): Promise<PartnerEntity | undefined>;
|
|
||||||
create(partner: PartnerIds): Promise<PartnerEntity>;
|
|
||||||
remove(partner: PartnerIds): Promise<void>;
|
|
||||||
update(partner: PartnerIds, entity: Updateable<Partners>): Promise<PartnerEntity>;
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
import { Insertable, Selectable, Updateable } from 'kysely';
|
|
||||||
import { AssetFaces, FaceSearch, Person } from 'src/db';
|
|
||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
|
||||||
import { SourceType } from 'src/enum';
|
|
||||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
|
||||||
import { FindOptionsRelations } from 'typeorm';
|
|
||||||
|
|
||||||
export const IPersonRepository = 'IPersonRepository';
|
|
||||||
|
|
||||||
export interface PersonSearchOptions {
|
|
||||||
minimumFaceCount: number;
|
|
||||||
withHidden: boolean;
|
|
||||||
closestFaceAssetId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PersonNameSearchOptions {
|
|
||||||
withHidden?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PersonNameResponse {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetFaceId {
|
|
||||||
assetId: string;
|
|
||||||
personId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateFacesData {
|
|
||||||
oldPersonId?: string;
|
|
||||||
faceIds?: string[];
|
|
||||||
newPersonId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PersonStatistics {
|
|
||||||
assets: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PeopleStatistics {
|
|
||||||
total: number;
|
|
||||||
hidden: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteFacesOptions {
|
|
||||||
sourceType: SourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UnassignFacesOptions = DeleteFacesOptions;
|
|
||||||
|
|
||||||
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[];
|
|
||||||
|
|
||||||
export interface IPersonRepository {
|
|
||||||
getAll(options?: Partial<PersonEntity>): AsyncIterableIterator<PersonEntity>;
|
|
||||||
getAllForUser(pagination: PaginationOptions, userId: string, options: PersonSearchOptions): Paginated<PersonEntity>;
|
|
||||||
getAllWithoutFaces(): Promise<PersonEntity[]>;
|
|
||||||
getById(personId: string): Promise<PersonEntity | null>;
|
|
||||||
getByName(userId: string, personName: string, options: PersonNameSearchOptions): Promise<PersonEntity[]>;
|
|
||||||
getDistinctNames(userId: string, options: PersonNameSearchOptions): Promise<PersonNameResponse[]>;
|
|
||||||
|
|
||||||
create(person: Insertable<Person>): Promise<PersonEntity>;
|
|
||||||
createAll(people: Insertable<Person>[]): Promise<string[]>;
|
|
||||||
delete(entities: PersonEntity[]): Promise<void>;
|
|
||||||
deleteFaces(options: DeleteFacesOptions): Promise<void>;
|
|
||||||
refreshFaces(
|
|
||||||
facesToAdd: Insertable<AssetFaces>[],
|
|
||||||
faceIdsToRemove: string[],
|
|
||||||
embeddingsToAdd?: Insertable<FaceSearch>[],
|
|
||||||
): Promise<void>;
|
|
||||||
getAllFaces(options?: Partial<AssetFaceEntity>): AsyncIterableIterator<AssetFaceEntity>;
|
|
||||||
getFaceById(id: string): Promise<AssetFaceEntity>;
|
|
||||||
getFaceByIdWithAssets(
|
|
||||||
id: string,
|
|
||||||
relations?: FindOptionsRelations<AssetFaceEntity>,
|
|
||||||
select?: SelectFaceOptions,
|
|
||||||
): Promise<AssetFaceEntity | undefined>;
|
|
||||||
getFaces(assetId: string): Promise<AssetFaceEntity[]>;
|
|
||||||
getFacesByIds(ids: AssetFaceId[]): Promise<AssetFaceEntity[]>;
|
|
||||||
getRandomFace(personId: string): Promise<AssetFaceEntity | undefined>;
|
|
||||||
getStatistics(personId: string): Promise<PersonStatistics>;
|
|
||||||
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
|
|
||||||
getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
|
|
||||||
reassignFaces(data: UpdateFacesData): Promise<number>;
|
|
||||||
unassignFaces(options: UnassignFacesOptions): Promise<void>;
|
|
||||||
update(person: Updateable<Person> & { id: string }): Promise<PersonEntity>;
|
|
||||||
updateAll(people: Insertable<Person>[]): Promise<void>;
|
|
||||||
getLatestFaceDate(): Promise<string | undefined>;
|
|
||||||
}
|
|
@ -1,213 +0,0 @@
|
|||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
|
||||||
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
|
||||||
import { AssetStatus, AssetType } from 'src/enum';
|
|
||||||
import { Paginated } from 'src/utils/pagination';
|
|
||||||
|
|
||||||
export const ISearchRepository = 'ISearchRepository';
|
|
||||||
|
|
||||||
export interface SearchResult<T> {
|
|
||||||
/** total matches */
|
|
||||||
total: number;
|
|
||||||
/** collection size */
|
|
||||||
count: number;
|
|
||||||
/** current page */
|
|
||||||
page: number;
|
|
||||||
/** items for page */
|
|
||||||
items: T[];
|
|
||||||
/** score */
|
|
||||||
distances: number[];
|
|
||||||
facets: SearchFacet[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchFacet {
|
|
||||||
fieldName: string;
|
|
||||||
counts: Array<{
|
|
||||||
count: number;
|
|
||||||
value: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SearchExploreItemSet<T> = Array<{
|
|
||||||
value: string;
|
|
||||||
data: T;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export interface SearchExploreItem<T> {
|
|
||||||
fieldName: string;
|
|
||||||
items: SearchExploreItemSet<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchAssetIDOptions {
|
|
||||||
checksum?: Buffer;
|
|
||||||
deviceAssetId?: string;
|
|
||||||
id?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchUserIdOptions {
|
|
||||||
deviceId?: string;
|
|
||||||
libraryId?: string | null;
|
|
||||||
userIds?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
|
|
||||||
|
|
||||||
export interface SearchStatusOptions {
|
|
||||||
isArchived?: boolean;
|
|
||||||
isEncoded?: boolean;
|
|
||||||
isFavorite?: boolean;
|
|
||||||
isMotion?: boolean;
|
|
||||||
isOffline?: boolean;
|
|
||||||
isVisible?: boolean;
|
|
||||||
isNotInAlbum?: boolean;
|
|
||||||
type?: AssetType;
|
|
||||||
status?: AssetStatus;
|
|
||||||
withArchived?: boolean;
|
|
||||||
withDeleted?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchOneToOneRelationOptions {
|
|
||||||
withExif?: boolean;
|
|
||||||
withStacked?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
|
||||||
withFaces?: boolean;
|
|
||||||
withPeople?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchDateOptions {
|
|
||||||
createdBefore?: Date;
|
|
||||||
createdAfter?: Date;
|
|
||||||
takenBefore?: Date;
|
|
||||||
takenAfter?: Date;
|
|
||||||
trashedBefore?: Date;
|
|
||||||
trashedAfter?: Date;
|
|
||||||
updatedBefore?: Date;
|
|
||||||
updatedAfter?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchPathOptions {
|
|
||||||
encodedVideoPath?: string;
|
|
||||||
originalFileName?: string;
|
|
||||||
originalPath?: string;
|
|
||||||
previewPath?: string;
|
|
||||||
thumbnailPath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchExifOptions {
|
|
||||||
city?: string | null;
|
|
||||||
country?: string | null;
|
|
||||||
lensModel?: string | null;
|
|
||||||
make?: string | null;
|
|
||||||
model?: string | null;
|
|
||||||
state?: string | null;
|
|
||||||
description?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchEmbeddingOptions {
|
|
||||||
embedding: string;
|
|
||||||
userIds: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchPeopleOptions {
|
|
||||||
personIds?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchTagOptions {
|
|
||||||
tagIds?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchOrderOptions {
|
|
||||||
orderDirection?: 'asc' | 'desc';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SearchPaginationOptions {
|
|
||||||
page: number;
|
|
||||||
size: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseAssetSearchOptions = SearchDateOptions &
|
|
||||||
SearchIdOptions &
|
|
||||||
SearchExifOptions &
|
|
||||||
SearchOrderOptions &
|
|
||||||
SearchPathOptions &
|
|
||||||
SearchStatusOptions &
|
|
||||||
SearchUserIdOptions &
|
|
||||||
SearchPeopleOptions &
|
|
||||||
SearchTagOptions;
|
|
||||||
|
|
||||||
export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions;
|
|
||||||
|
|
||||||
export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions;
|
|
||||||
|
|
||||||
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
|
|
||||||
|
|
||||||
export type SmartSearchOptions = SearchDateOptions &
|
|
||||||
SearchEmbeddingOptions &
|
|
||||||
SearchExifOptions &
|
|
||||||
SearchOneToOneRelationOptions &
|
|
||||||
SearchStatusOptions &
|
|
||||||
SearchUserIdOptions &
|
|
||||||
SearchPeopleOptions &
|
|
||||||
SearchTagOptions;
|
|
||||||
|
|
||||||
export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
|
|
||||||
hasPerson?: boolean;
|
|
||||||
numResults: number;
|
|
||||||
maxDistance: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetDuplicateSearch {
|
|
||||||
assetId: string;
|
|
||||||
embedding: string;
|
|
||||||
maxDistance: number;
|
|
||||||
type: AssetType;
|
|
||||||
userIds: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FaceSearchResult {
|
|
||||||
distance: number;
|
|
||||||
id: string;
|
|
||||||
personId: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetDuplicateResult {
|
|
||||||
assetId: string;
|
|
||||||
duplicateId: string | null;
|
|
||||||
distance: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetStatesOptions {
|
|
||||||
country?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetCitiesOptions extends GetStatesOptions {
|
|
||||||
state?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetCameraModelsOptions {
|
|
||||||
make?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetCameraMakesOptions {
|
|
||||||
model?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISearchRepository {
|
|
||||||
searchMetadata(pagination: SearchPaginationOptions, options: AssetSearchOptions): Paginated<AssetEntity>;
|
|
||||||
searchSmart(pagination: SearchPaginationOptions, options: SmartSearchOptions): Paginated<AssetEntity>;
|
|
||||||
searchDuplicates(options: AssetDuplicateSearch): Promise<AssetDuplicateResult[]>;
|
|
||||||
searchFaces(search: FaceEmbeddingSearch): Promise<FaceSearchResult[]>;
|
|
||||||
searchRandom(size: number, options: AssetSearchOptions): Promise<AssetEntity[]>;
|
|
||||||
upsert(assetId: string, embedding: string): Promise<void>;
|
|
||||||
searchPlaces(placeName: string): Promise<GeodataPlacesEntity[]>;
|
|
||||||
getAssetsByCity(userIds: string[]): Promise<AssetEntity[]>;
|
|
||||||
deleteAllSearchEmbeddings(): Promise<void>;
|
|
||||||
getDimensionSize(): Promise<number>;
|
|
||||||
setDimensionSize(dimSize: number): Promise<void>;
|
|
||||||
getCountries(userIds: string[]): Promise<Array<string | null>>;
|
|
||||||
getStates(userIds: string[], options: GetStatesOptions): Promise<Array<string | null>>;
|
|
||||||
getCities(userIds: string[], options: GetCitiesOptions): Promise<Array<string | null>>;
|
|
||||||
getCameraMakes(userIds: string[], options: GetCameraMakesOptions): Promise<Array<string | null>>;
|
|
||||||
getCameraModels(userIds: string[], options: GetCameraModelsOptions): Promise<Array<string | null>>;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { SharedLinks } from 'src/db';
|
|
||||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
|
||||||
|
|
||||||
export const ISharedLinkRepository = 'ISharedLinkRepository';
|
|
||||||
|
|
||||||
export type SharedLinkSearchOptions = {
|
|
||||||
userId: string;
|
|
||||||
albumId?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ISharedLinkRepository {
|
|
||||||
getAll(options: SharedLinkSearchOptions): Promise<SharedLinkEntity[]>;
|
|
||||||
get(userId: string, id: string): Promise<SharedLinkEntity | undefined>;
|
|
||||||
getByKey(key: Buffer): Promise<SharedLinkEntity | undefined>;
|
|
||||||
create(entity: Insertable<SharedLinks> & { assetIds?: string[] }): Promise<SharedLinkEntity>;
|
|
||||||
update(entity: Updateable<SharedLinks> & { id: string; assetIds?: string[] }): Promise<SharedLinkEntity>;
|
|
||||||
remove(entity: SharedLinkEntity): Promise<void>;
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { Updateable } from 'kysely';
|
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
|
||||||
|
|
||||||
export const IStackRepository = 'IStackRepository';
|
|
||||||
|
|
||||||
export interface StackSearch {
|
|
||||||
ownerId: string;
|
|
||||||
primaryAssetId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IStackRepository {
|
|
||||||
search(query: StackSearch): Promise<StackEntity[]>;
|
|
||||||
create(stack: { ownerId: string; assetIds: string[] }): Promise<StackEntity>;
|
|
||||||
update(id: string, entity: Updateable<StackEntity>): Promise<StackEntity>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
deleteAll(ids: string[]): Promise<void>;
|
|
||||||
getById(id: string): Promise<StackEntity | undefined>;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
import { WatchOptions } from 'chokidar';
|
|
||||||
import { Stats } from 'node:fs';
|
|
||||||
import { FileReadOptions } from 'node:fs/promises';
|
|
||||||
import { Readable, Writable } from 'node:stream';
|
|
||||||
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
|
||||||
|
|
||||||
export interface ImmichReadStream {
|
|
||||||
stream: Readable;
|
|
||||||
type?: string;
|
|
||||||
length?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ImmichZipStream extends ImmichReadStream {
|
|
||||||
addFile: (inputPath: string, filename: string) => void;
|
|
||||||
finalize: () => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DiskUsage {
|
|
||||||
available: number;
|
|
||||||
free: number;
|
|
||||||
total: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IStorageRepository = 'IStorageRepository';
|
|
||||||
|
|
||||||
export interface WatchEvents {
|
|
||||||
onReady(): void;
|
|
||||||
onAdd(path: string): void;
|
|
||||||
onChange(path: string): void;
|
|
||||||
onUnlink(path: string): void;
|
|
||||||
onError(error: Error): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IStorageRepository {
|
|
||||||
createZipStream(): ImmichZipStream;
|
|
||||||
createReadStream(filepath: string, mimeType?: string | null): Promise<ImmichReadStream>;
|
|
||||||
readFile(filepath: string, options?: FileReadOptions<Buffer>): Promise<Buffer>;
|
|
||||||
createFile(filepath: string, buffer: Buffer): Promise<void>;
|
|
||||||
createWriteStream(filepath: string): Writable;
|
|
||||||
createOrOverwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
|
||||||
overwriteFile(filepath: string, buffer: Buffer): Promise<void>;
|
|
||||||
realpath(filepath: string): Promise<string>;
|
|
||||||
unlink(filepath: string): Promise<void>;
|
|
||||||
unlinkDir(folder: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>;
|
|
||||||
removeEmptyDirs(folder: string, self?: boolean): Promise<void>;
|
|
||||||
checkFileExists(filepath: string, mode?: number): Promise<boolean>;
|
|
||||||
mkdirSync(filepath: string): void;
|
|
||||||
checkDiskUsage(folder: string): Promise<DiskUsage>;
|
|
||||||
readdir(folder: string): Promise<string[]>;
|
|
||||||
stat(filepath: string): Promise<Stats>;
|
|
||||||
crawl(options: CrawlOptionsDto): Promise<string[]>;
|
|
||||||
walk(options: WalkOptionsDto): AsyncGenerator<string[]>;
|
|
||||||
copyFile(source: string, target: string): Promise<void>;
|
|
||||||
rename(source: string, target: string): Promise<void>;
|
|
||||||
watch(paths: string[], options: WatchOptions, events: Partial<WatchEvents>): () => Promise<void>;
|
|
||||||
utimes(filepath: string, atime: Date, mtime: Date): Promise<void>;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import { TagEntity } from 'src/entities/tag.entity';
|
|
||||||
import { IBulkAsset } from 'src/utils/asset.util';
|
|
||||||
|
|
||||||
export const ITagRepository = 'ITagRepository';
|
|
||||||
|
|
||||||
export type AssetTagItem = { assetId: string; tagId: string };
|
|
||||||
|
|
||||||
export interface ITagRepository extends IBulkAsset {
|
|
||||||
getAll(userId: string): Promise<TagEntity[]>;
|
|
||||||
getByValue(userId: string, value: string): Promise<TagEntity | null>;
|
|
||||||
upsertValue(request: { userId: string; value: string; parent?: TagEntity }): Promise<TagEntity>;
|
|
||||||
|
|
||||||
create(tag: Partial<TagEntity>): Promise<TagEntity>;
|
|
||||||
get(id: string): Promise<TagEntity | null>;
|
|
||||||
update(tag: { id: string } & Partial<TagEntity>): Promise<TagEntity>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
|
|
||||||
upsertAssetTags({ assetId, tagIds }: { assetId: string; tagIds: string[] }): Promise<void>;
|
|
||||||
upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]>;
|
|
||||||
deleteEmptyTags(): Promise<void>;
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
import { Insertable, Updateable } from 'kysely';
|
|
||||||
import { Users } from 'src/db';
|
|
||||||
import { UserMetadata } from 'src/entities/user-metadata.entity';
|
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
|
||||||
|
|
||||||
export interface UserListFilter {
|
|
||||||
withDeleted?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserStatsQueryResponse {
|
|
||||||
userId: string;
|
|
||||||
userName: string;
|
|
||||||
photos: number;
|
|
||||||
videos: number;
|
|
||||||
usage: number;
|
|
||||||
usagePhotos: number;
|
|
||||||
usageVideos: number;
|
|
||||||
quotaSizeInBytes: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserFindOptions {
|
|
||||||
withDeleted?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IUserRepository = 'IUserRepository';
|
|
||||||
|
|
||||||
export interface IUserRepository {
|
|
||||||
get(id: string, options: UserFindOptions): Promise<UserEntity | undefined>;
|
|
||||||
getAdmin(): Promise<UserEntity | undefined>;
|
|
||||||
hasAdmin(): Promise<boolean>;
|
|
||||||
getByEmail(email: string, withPassword?: boolean): Promise<UserEntity | undefined>;
|
|
||||||
getByStorageLabel(storageLabel: string): Promise<UserEntity | undefined>;
|
|
||||||
getByOAuthId(oauthId: string): Promise<UserEntity | undefined>;
|
|
||||||
getDeletedUsers(): Promise<UserEntity[]>;
|
|
||||||
getList(filter?: UserListFilter): Promise<UserEntity[]>;
|
|
||||||
getUserStats(): Promise<UserStatsQueryResponse[]>;
|
|
||||||
create(user: Insertable<Users>): Promise<UserEntity>;
|
|
||||||
update(id: string, user: Updateable<Users>): Promise<UserEntity>;
|
|
||||||
restore(id: string): Promise<UserEntity>;
|
|
||||||
upsertMetadata<T extends keyof UserMetadata>(id: string, item: { key: T; value: UserMetadata[T] }): Promise<void>;
|
|
||||||
deleteMetadata<T extends keyof UserMetadata>(id: string, key: T): Promise<void>;
|
|
||||||
delete(user: UserEntity, hard?: boolean): Promise<UserEntity>;
|
|
||||||
updateUsage(id: string, delta: number): Promise<void>;
|
|
||||||
syncUsage(id?: string): Promise<void>;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/enum';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/enum';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/enum';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
@ -6,7 +6,17 @@ import { Albums, DB } from 'src/db';
|
|||||||
import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
import { Chunked, ChunkedArray, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
import { AlbumUserCreateDto } from 'src/dtos/album.dto';
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
import { AlbumEntity } from 'src/entities/album.entity';
|
||||||
import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfaces/album.interface';
|
|
||||||
|
export interface AlbumAssetCount {
|
||||||
|
albumId: string;
|
||||||
|
assetCount: number;
|
||||||
|
startDate: Date | null;
|
||||||
|
endDate: Date | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AlbumInfoOptions {
|
||||||
|
withAssets: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const userColumns = [
|
const userColumns = [
|
||||||
'id',
|
'id',
|
||||||
@ -71,7 +81,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AlbumRepository implements IAlbumRepository {
|
export class AlbumRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, { withAssets: true }] })
|
@GenerateSql({ params: [DummyValue.UUID, { withAssets: true }] })
|
||||||
|
@ -21,34 +21,138 @@ import {
|
|||||||
withTagId,
|
withTagId,
|
||||||
withTags,
|
withTags,
|
||||||
} from 'src/entities/asset.entity';
|
} from 'src/entities/asset.entity';
|
||||||
import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
|
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
|
||||||
import {
|
|
||||||
AssetDeltaSyncOptions,
|
|
||||||
AssetExploreFieldOptions,
|
|
||||||
AssetFullSyncOptions,
|
|
||||||
AssetGetByChecksumOptions,
|
|
||||||
AssetStats,
|
|
||||||
AssetStatsOptions,
|
|
||||||
AssetUpdateDuplicateOptions,
|
|
||||||
DayOfYearAssets,
|
|
||||||
DuplicateGroup,
|
|
||||||
GetByIdsRelations,
|
|
||||||
IAssetRepository,
|
|
||||||
LivePhotoSearchOptions,
|
|
||||||
MonthDay,
|
|
||||||
TimeBucketItem,
|
|
||||||
TimeBucketOptions,
|
|
||||||
TimeBucketSize,
|
|
||||||
WithProperty,
|
|
||||||
WithoutProperty,
|
|
||||||
} from 'src/interfaces/asset.interface';
|
|
||||||
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/interfaces/search.interface';
|
|
||||||
import { MapMarker, MapMarkerSearchOptions } from 'src/repositories/map.repository';
|
import { MapMarker, MapMarkerSearchOptions } from 'src/repositories/map.repository';
|
||||||
|
import { AssetSearchOptions, SearchExploreItem, SearchExploreItemSet } from 'src/repositories/search.repository';
|
||||||
import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database';
|
import { anyUuid, asUuid, mapUpsertColumns } from 'src/utils/database';
|
||||||
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
import { Paginated, PaginationOptions, paginationHelper } from 'src/utils/pagination';
|
||||||
|
|
||||||
|
export type AssetStats = Record<AssetType, number>;
|
||||||
|
|
||||||
|
export interface AssetStatsOptions {
|
||||||
|
isFavorite?: boolean;
|
||||||
|
isArchived?: boolean;
|
||||||
|
isTrashed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LivePhotoSearchOptions {
|
||||||
|
ownerId: string;
|
||||||
|
libraryId?: string | null;
|
||||||
|
livePhotoCID: string;
|
||||||
|
otherAssetId: string;
|
||||||
|
type: AssetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WithoutProperty {
|
||||||
|
THUMBNAIL = 'thumbnail',
|
||||||
|
ENCODED_VIDEO = 'encoded-video',
|
||||||
|
EXIF = 'exif',
|
||||||
|
SMART_SEARCH = 'smart-search',
|
||||||
|
DUPLICATE = 'duplicate',
|
||||||
|
FACES = 'faces',
|
||||||
|
SIDECAR = 'sidecar',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WithProperty {
|
||||||
|
SIDECAR = 'sidecar',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TimeBucketSize {
|
||||||
|
DAY = 'DAY',
|
||||||
|
MONTH = 'MONTH',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetBuilderOptions {
|
||||||
|
isArchived?: boolean;
|
||||||
|
isFavorite?: boolean;
|
||||||
|
isTrashed?: boolean;
|
||||||
|
isDuplicate?: boolean;
|
||||||
|
albumId?: string;
|
||||||
|
tagId?: string;
|
||||||
|
personId?: string;
|
||||||
|
userIds?: string[];
|
||||||
|
withStacked?: boolean;
|
||||||
|
exifInfo?: boolean;
|
||||||
|
status?: AssetStatus;
|
||||||
|
assetType?: AssetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeBucketOptions extends AssetBuilderOptions {
|
||||||
|
size: TimeBucketSize;
|
||||||
|
order?: AssetOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeBucketItem {
|
||||||
|
timeBucket: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MonthDay {
|
||||||
|
day: number;
|
||||||
|
month: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetExploreFieldOptions {
|
||||||
|
maxFields: number;
|
||||||
|
minAssetsPerField: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetFullSyncOptions {
|
||||||
|
ownerId: string;
|
||||||
|
lastId?: string;
|
||||||
|
updatedUntil: Date;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetDeltaSyncOptions {
|
||||||
|
userIds: string[];
|
||||||
|
updatedAfter: Date;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetUpdateDuplicateOptions {
|
||||||
|
targetDuplicateId: string | null;
|
||||||
|
assetIds: string[];
|
||||||
|
duplicateIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpsertFileOptions {
|
||||||
|
assetId: string;
|
||||||
|
type: AssetFileType;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetGetByChecksumOptions {
|
||||||
|
ownerId: string;
|
||||||
|
checksum: Buffer;
|
||||||
|
libraryId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssetPathEntity = Pick<AssetEntity, 'id' | 'originalPath' | 'isOffline'>;
|
||||||
|
|
||||||
|
export interface GetByIdsRelations {
|
||||||
|
exifInfo?: boolean;
|
||||||
|
faces?: { person?: boolean };
|
||||||
|
files?: boolean;
|
||||||
|
library?: boolean;
|
||||||
|
owner?: boolean;
|
||||||
|
smartSearch?: boolean;
|
||||||
|
stack?: { assets?: boolean };
|
||||||
|
tags?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DuplicateGroup {
|
||||||
|
duplicateId: string;
|
||||||
|
assets: AssetEntity[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DayOfYearAssets {
|
||||||
|
yearsAgo: number;
|
||||||
|
assets: AssetEntity[];
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AssetRepository implements IAssetRepository {
|
export class AssetRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
async upsertExif(exif: Insertable<Exif>): Promise<void> {
|
async upsertExif(exif: Insertable<Exif>): Promise<void> {
|
||||||
|
@ -13,9 +13,9 @@ import { Notice } from 'postgres';
|
|||||||
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
||||||
import { Telemetry } from 'src/decorators';
|
import { Telemetry } from 'src/decorators';
|
||||||
import { EnvDto } from 'src/dtos/env.dto';
|
import { EnvDto } from 'src/dtos/env.dto';
|
||||||
import { ImmichEnvironment, ImmichHeader, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
|
import { DatabaseExtension, ImmichEnvironment, ImmichHeader, ImmichTelemetry, ImmichWorker, LogLevel } from 'src/enum';
|
||||||
import { DatabaseConnectionParams, DatabaseExtension, VectorExtension } from 'src/interfaces/database.interface';
|
|
||||||
import { QueueName } from 'src/interfaces/job.interface';
|
import { QueueName } from 'src/interfaces/job.interface';
|
||||||
|
import { DatabaseConnectionParams, VectorExtension } from 'src/repositories/database.repository';
|
||||||
import { setDifference } from 'src/utils/set';
|
import { setDifference } from 'src/utils/set';
|
||||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
||||||
|
|
||||||
|
@ -2,11 +2,10 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { compareSync, hash } from 'bcrypt';
|
import { compareSync, hash } from 'bcrypt';
|
||||||
import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from 'node:crypto';
|
import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from 'node:crypto';
|
||||||
import { createReadStream } from 'node:fs';
|
import { createReadStream } from 'node:fs';
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CryptoRepository implements ICryptoRepository {
|
export class CryptoRepository {
|
||||||
randomUUID() {
|
randomUUID(): string {
|
||||||
return randomUUID();
|
return randomUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,24 +6,66 @@ import { InjectKysely } from 'nestjs-kysely';
|
|||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
import { POSTGRES_VERSION_RANGE, VECTOR_VERSION_RANGE, VECTORS_VERSION_RANGE } from 'src/constants';
|
||||||
import { DB } from 'src/db';
|
import { DB } from 'src/db';
|
||||||
import {
|
import { DatabaseExtension } from 'src/enum';
|
||||||
DatabaseExtension,
|
|
||||||
DatabaseLock,
|
|
||||||
EXTENSION_NAMES,
|
|
||||||
ExtensionVersion,
|
|
||||||
IDatabaseRepository,
|
|
||||||
VectorExtension,
|
|
||||||
VectorIndex,
|
|
||||||
VectorUpdateResult,
|
|
||||||
} from 'src/interfaces/database.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 { UPSERT_COLUMNS } from 'src/utils/database';
|
import { UPSERT_COLUMNS } from 'src/utils/database';
|
||||||
import { isValidInteger } from 'src/validation';
|
import { isValidInteger } from 'src/validation';
|
||||||
import { DataSource, EntityManager, EntityMetadata, QueryRunner } from 'typeorm';
|
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()
|
@Injectable()
|
||||||
export class DatabaseRepository implements IDatabaseRepository {
|
export class DatabaseRepository {
|
||||||
private vectorExtension: VectorExtension;
|
private vectorExtension: VectorExtension;
|
||||||
private readonly asyncLock = new AsyncLock();
|
private readonly asyncLock = new AsyncLock();
|
||||||
|
|
||||||
|
@ -1,20 +1,6 @@
|
|||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|
||||||
import { IDatabaseRepository } from 'src/interfaces/database.interface';
|
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
|
||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
|
||||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
|
||||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
|
||||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
|
||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
|
||||||
import { IUserRepository } from 'src/interfaces/user.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';
|
||||||
@ -58,44 +44,44 @@ import { ViewRepository } from 'src/repositories/view-repository';
|
|||||||
export const repositories = [
|
export const repositories = [
|
||||||
AccessRepository,
|
AccessRepository,
|
||||||
ActivityRepository,
|
ActivityRepository,
|
||||||
|
AlbumRepository,
|
||||||
AlbumUserRepository,
|
AlbumUserRepository,
|
||||||
AuditRepository,
|
AuditRepository,
|
||||||
ApiKeyRepository,
|
ApiKeyRepository,
|
||||||
|
AssetRepository,
|
||||||
ConfigRepository,
|
ConfigRepository,
|
||||||
CronRepository,
|
CronRepository,
|
||||||
|
CryptoRepository,
|
||||||
|
DatabaseRepository,
|
||||||
|
LibraryRepository,
|
||||||
LoggingRepository,
|
LoggingRepository,
|
||||||
MapRepository,
|
MapRepository,
|
||||||
MediaRepository,
|
MediaRepository,
|
||||||
MemoryRepository,
|
MemoryRepository,
|
||||||
MetadataRepository,
|
MetadataRepository,
|
||||||
|
MoveRepository,
|
||||||
NotificationRepository,
|
NotificationRepository,
|
||||||
OAuthRepository,
|
OAuthRepository,
|
||||||
|
PartnerRepository,
|
||||||
|
PersonRepository,
|
||||||
ProcessRepository,
|
ProcessRepository,
|
||||||
|
SearchRepository,
|
||||||
SessionRepository,
|
SessionRepository,
|
||||||
ServerInfoRepository,
|
ServerInfoRepository,
|
||||||
|
SharedLinkRepository,
|
||||||
|
StackRepository,
|
||||||
|
StorageRepository,
|
||||||
SystemMetadataRepository,
|
SystemMetadataRepository,
|
||||||
|
TagRepository,
|
||||||
TelemetryRepository,
|
TelemetryRepository,
|
||||||
TrashRepository,
|
TrashRepository,
|
||||||
|
UserRepository,
|
||||||
ViewRepository,
|
ViewRepository,
|
||||||
VersionHistoryRepository,
|
VersionHistoryRepository,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const providers = [
|
export const providers = [
|
||||||
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
|
||||||
{ provide: IAssetRepository, useClass: AssetRepository },
|
|
||||||
{ provide: ICryptoRepository, useClass: CryptoRepository },
|
|
||||||
{ provide: IDatabaseRepository, useClass: DatabaseRepository },
|
|
||||||
{ provide: IEventRepository, useClass: EventRepository },
|
{ provide: IEventRepository, useClass: EventRepository },
|
||||||
{ provide: IJobRepository, useClass: JobRepository },
|
{ provide: IJobRepository, useClass: JobRepository },
|
||||||
{ provide: ILibraryRepository, useClass: LibraryRepository },
|
|
||||||
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
||||||
{ provide: IMoveRepository, useClass: MoveRepository },
|
|
||||||
{ provide: IPartnerRepository, useClass: PartnerRepository },
|
|
||||||
{ provide: IPersonRepository, useClass: PersonRepository },
|
|
||||||
{ provide: ISearchRepository, useClass: SearchRepository },
|
|
||||||
{ provide: ISharedLinkRepository, useClass: SharedLinkRepository },
|
|
||||||
{ provide: IStackRepository, useClass: StackRepository },
|
|
||||||
{ provide: IStorageRepository, useClass: StorageRepository },
|
|
||||||
{ provide: ITagRepository, useClass: TagRepository },
|
|
||||||
{ provide: IUserRepository, useClass: UserRepository },
|
|
||||||
];
|
];
|
||||||
|
@ -7,7 +7,6 @@ import { DummyValue, GenerateSql } from 'src/decorators';
|
|||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
import { AssetType } from 'src/enum';
|
import { AssetType } from 'src/enum';
|
||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
|
||||||
|
|
||||||
const userColumns = [
|
const userColumns = [
|
||||||
'users.id',
|
'users.id',
|
||||||
@ -34,7 +33,7 @@ const withOwner = (eb: ExpressionBuilder<DB, 'libraries'>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LibraryRepository implements ILibraryRepository {
|
export class LibraryRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
|
@ -5,10 +5,11 @@ import { DB, MoveHistory } from 'src/db';
|
|||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { MoveEntity } from 'src/entities/move.entity';
|
import { MoveEntity } from 'src/entities/move.entity';
|
||||||
import { PathType } from 'src/enum';
|
import { PathType } from 'src/enum';
|
||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
|
||||||
|
export type MoveCreate = Pick<MoveEntity, 'oldPath' | 'newPath' | 'entityId' | 'pathType'> & Partial<MoveEntity>;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MoveRepository implements IMoveRepository {
|
export class MoveRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
create(entity: Insertable<MoveHistory>): Promise<MoveEntity> {
|
create(entity: Insertable<MoveHistory>): Promise<MoveEntity> {
|
||||||
|
@ -5,7 +5,16 @@ import { InjectKysely } from 'nestjs-kysely';
|
|||||||
import { DB, Partners, Users } from 'src/db';
|
import { DB, Partners, Users } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||||
import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.interface';
|
|
||||||
|
export interface PartnerIds {
|
||||||
|
sharedById: string;
|
||||||
|
sharedWithId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PartnerDirection {
|
||||||
|
SharedBy = 'shared-by',
|
||||||
|
SharedWith = 'shared-with',
|
||||||
|
}
|
||||||
|
|
||||||
const columns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const;
|
const columns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const;
|
||||||
|
|
||||||
@ -28,7 +37,7 @@ const withSharedWith = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PartnerRepository implements IPartnerRepository {
|
export class PartnerRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ExpressionBuilder, Insertable, Kysely, sql } from 'kysely';
|
import { ExpressionBuilder, Insertable, Kysely, Selectable, sql } from 'kysely';
|
||||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { AssetFaces, DB, FaceSearch, Person } from 'src/db';
|
import { AssetFaces, DB, FaceSearch, Person } from 'src/db';
|
||||||
@ -7,23 +7,53 @@ import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
|||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
import { PersonEntity } from 'src/entities/person.entity';
|
||||||
import { SourceType } from 'src/enum';
|
import { SourceType } from 'src/enum';
|
||||||
import {
|
|
||||||
AssetFaceId,
|
|
||||||
DeleteFacesOptions,
|
|
||||||
IPersonRepository,
|
|
||||||
PeopleStatistics,
|
|
||||||
PersonNameResponse,
|
|
||||||
PersonNameSearchOptions,
|
|
||||||
PersonSearchOptions,
|
|
||||||
PersonStatistics,
|
|
||||||
SelectFaceOptions,
|
|
||||||
UnassignFacesOptions,
|
|
||||||
UpdateFacesData,
|
|
||||||
} from 'src/interfaces/person.interface';
|
|
||||||
import { mapUpsertColumns } from 'src/utils/database';
|
import { mapUpsertColumns } from 'src/utils/database';
|
||||||
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
import { Paginated, PaginationOptions } from 'src/utils/pagination';
|
||||||
import { FindOptionsRelations } from 'typeorm';
|
import { FindOptionsRelations } from 'typeorm';
|
||||||
|
|
||||||
|
export interface PersonSearchOptions {
|
||||||
|
minimumFaceCount: number;
|
||||||
|
withHidden: boolean;
|
||||||
|
closestFaceAssetId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonNameSearchOptions {
|
||||||
|
withHidden?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonNameResponse {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetFaceId {
|
||||||
|
assetId: string;
|
||||||
|
personId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateFacesData {
|
||||||
|
oldPersonId?: string;
|
||||||
|
faceIds?: string[];
|
||||||
|
newPersonId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonStatistics {
|
||||||
|
assets: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeopleStatistics {
|
||||||
|
total: number;
|
||||||
|
hidden: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteFacesOptions {
|
||||||
|
sourceType: SourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UnassignFacesOptions = DeleteFacesOptions;
|
||||||
|
|
||||||
|
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[];
|
||||||
|
|
||||||
const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
const withPerson = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
||||||
return jsonObjectFrom(
|
return jsonObjectFrom(
|
||||||
eb.selectFrom('person').selectAll('person').whereRef('person.id', '=', 'asset_faces.personId'),
|
eb.selectFrom('person').selectAll('person').whereRef('person.id', '=', 'asset_faces.personId'),
|
||||||
@ -43,7 +73,7 @@ const withFaceSearch = (eb: ExpressionBuilder<DB, 'asset_faces'>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PersonRepository implements IPersonRepository {
|
export class PersonRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ oldPersonId: DummyValue.UUID, newPersonId: DummyValue.UUID }] })
|
@GenerateSql({ params: [{ oldPersonId: DummyValue.UUID, newPersonId: DummyValue.UUID }] })
|
||||||
|
@ -6,26 +6,202 @@ import { DB } from 'src/db';
|
|||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity';
|
import { AssetEntity, searchAssetBuilder } from 'src/entities/asset.entity';
|
||||||
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity';
|
||||||
import { AssetType } from 'src/enum';
|
import { AssetStatus, AssetType } from 'src/enum';
|
||||||
import {
|
|
||||||
AssetDuplicateSearch,
|
|
||||||
AssetSearchOptions,
|
|
||||||
FaceEmbeddingSearch,
|
|
||||||
GetCameraMakesOptions,
|
|
||||||
GetCameraModelsOptions,
|
|
||||||
GetCitiesOptions,
|
|
||||||
GetStatesOptions,
|
|
||||||
ISearchRepository,
|
|
||||||
SearchPaginationOptions,
|
|
||||||
SmartSearchOptions,
|
|
||||||
} from 'src/interfaces/search.interface';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { anyUuid, asUuid } from 'src/utils/database';
|
import { anyUuid, asUuid } from 'src/utils/database';
|
||||||
import { Paginated } from 'src/utils/pagination';
|
import { Paginated } from 'src/utils/pagination';
|
||||||
import { isValidInteger } from 'src/validation';
|
import { isValidInteger } from 'src/validation';
|
||||||
|
|
||||||
|
export interface SearchResult<T> {
|
||||||
|
/** total matches */
|
||||||
|
total: number;
|
||||||
|
/** collection size */
|
||||||
|
count: number;
|
||||||
|
/** current page */
|
||||||
|
page: number;
|
||||||
|
/** items for page */
|
||||||
|
items: T[];
|
||||||
|
/** score */
|
||||||
|
distances: number[];
|
||||||
|
facets: SearchFacet[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchFacet {
|
||||||
|
fieldName: string;
|
||||||
|
counts: Array<{
|
||||||
|
count: number;
|
||||||
|
value: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SearchExploreItemSet<T> = Array<{
|
||||||
|
value: string;
|
||||||
|
data: T;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export interface SearchExploreItem<T> {
|
||||||
|
fieldName: string;
|
||||||
|
items: SearchExploreItemSet<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchAssetIDOptions {
|
||||||
|
checksum?: Buffer;
|
||||||
|
deviceAssetId?: string;
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchUserIdOptions {
|
||||||
|
deviceId?: string;
|
||||||
|
libraryId?: string | null;
|
||||||
|
userIds?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
|
||||||
|
|
||||||
|
export interface SearchStatusOptions {
|
||||||
|
isArchived?: boolean;
|
||||||
|
isEncoded?: boolean;
|
||||||
|
isFavorite?: boolean;
|
||||||
|
isMotion?: boolean;
|
||||||
|
isOffline?: boolean;
|
||||||
|
isVisible?: boolean;
|
||||||
|
isNotInAlbum?: boolean;
|
||||||
|
type?: AssetType;
|
||||||
|
status?: AssetStatus;
|
||||||
|
withArchived?: boolean;
|
||||||
|
withDeleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchOneToOneRelationOptions {
|
||||||
|
withExif?: boolean;
|
||||||
|
withStacked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
||||||
|
withFaces?: boolean;
|
||||||
|
withPeople?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchDateOptions {
|
||||||
|
createdBefore?: Date;
|
||||||
|
createdAfter?: Date;
|
||||||
|
takenBefore?: Date;
|
||||||
|
takenAfter?: Date;
|
||||||
|
trashedBefore?: Date;
|
||||||
|
trashedAfter?: Date;
|
||||||
|
updatedBefore?: Date;
|
||||||
|
updatedAfter?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchPathOptions {
|
||||||
|
encodedVideoPath?: string;
|
||||||
|
originalFileName?: string;
|
||||||
|
originalPath?: string;
|
||||||
|
previewPath?: string;
|
||||||
|
thumbnailPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchExifOptions {
|
||||||
|
city?: string | null;
|
||||||
|
country?: string | null;
|
||||||
|
lensModel?: string | null;
|
||||||
|
make?: string | null;
|
||||||
|
model?: string | null;
|
||||||
|
state?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchEmbeddingOptions {
|
||||||
|
embedding: string;
|
||||||
|
userIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchPeopleOptions {
|
||||||
|
personIds?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchTagOptions {
|
||||||
|
tagIds?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchOrderOptions {
|
||||||
|
orderDirection?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchPaginationOptions {
|
||||||
|
page: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseAssetSearchOptions = SearchDateOptions &
|
||||||
|
SearchIdOptions &
|
||||||
|
SearchExifOptions &
|
||||||
|
SearchOrderOptions &
|
||||||
|
SearchPathOptions &
|
||||||
|
SearchStatusOptions &
|
||||||
|
SearchUserIdOptions &
|
||||||
|
SearchPeopleOptions &
|
||||||
|
SearchTagOptions;
|
||||||
|
|
||||||
|
export type AssetSearchOptions = BaseAssetSearchOptions & SearchRelationOptions;
|
||||||
|
|
||||||
|
export type AssetSearchOneToOneRelationOptions = BaseAssetSearchOptions & SearchOneToOneRelationOptions;
|
||||||
|
|
||||||
|
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
|
||||||
|
|
||||||
|
export type SmartSearchOptions = SearchDateOptions &
|
||||||
|
SearchEmbeddingOptions &
|
||||||
|
SearchExifOptions &
|
||||||
|
SearchOneToOneRelationOptions &
|
||||||
|
SearchStatusOptions &
|
||||||
|
SearchUserIdOptions &
|
||||||
|
SearchPeopleOptions &
|
||||||
|
SearchTagOptions;
|
||||||
|
|
||||||
|
export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
|
||||||
|
hasPerson?: boolean;
|
||||||
|
numResults: number;
|
||||||
|
maxDistance: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetDuplicateSearch {
|
||||||
|
assetId: string;
|
||||||
|
embedding: string;
|
||||||
|
maxDistance: number;
|
||||||
|
type: AssetType;
|
||||||
|
userIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FaceSearchResult {
|
||||||
|
distance: number;
|
||||||
|
id: string;
|
||||||
|
personId: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetDuplicateResult {
|
||||||
|
assetId: string;
|
||||||
|
duplicateId: string | null;
|
||||||
|
distance: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetStatesOptions {
|
||||||
|
country?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetCitiesOptions extends GetStatesOptions {
|
||||||
|
state?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetCameraModelsOptions {
|
||||||
|
make?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetCameraMakesOptions {
|
||||||
|
model?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchRepository implements ISearchRepository {
|
export class SearchRepository {
|
||||||
constructor(
|
constructor(
|
||||||
private logger: LoggingRepository,
|
private logger: LoggingRepository,
|
||||||
@InjectKysely() private db: Kysely<DB>,
|
@InjectKysely() private db: Kysely<DB>,
|
||||||
|
@ -7,10 +7,14 @@ import { DB, SharedLinks } from 'src/db';
|
|||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||||
import { SharedLinkType } from 'src/enum';
|
import { SharedLinkType } from 'src/enum';
|
||||||
import { ISharedLinkRepository, SharedLinkSearchOptions } from 'src/interfaces/shared-link.interface';
|
|
||||||
|
export type SharedLinkSearchOptions = {
|
||||||
|
userId: string;
|
||||||
|
albumId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SharedLinkRepository implements ISharedLinkRepository {
|
export class SharedLinkRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||||
|
@ -5,9 +5,13 @@ import { InjectKysely } from 'nestjs-kysely';
|
|||||||
import { DB } from 'src/db';
|
import { DB } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
import { StackEntity } from 'src/entities/stack.entity';
|
||||||
import { IStackRepository, StackSearch } from 'src/interfaces/stack.interface';
|
|
||||||
import { asUuid } from 'src/utils/database';
|
import { asUuid } from 'src/utils/database';
|
||||||
|
|
||||||
|
export interface StackSearch {
|
||||||
|
ownerId: string;
|
||||||
|
primaryAssetId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false) => {
|
const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false) => {
|
||||||
return jsonArrayFrom(
|
return jsonArrayFrom(
|
||||||
eb
|
eb
|
||||||
@ -35,7 +39,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'asset_stack'>, withTags = false)
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StackRepository implements IStackRepository {
|
export class StackRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
||||||
|
@ -5,20 +5,38 @@ import { escapePath, glob, globStream } from 'fast-glob';
|
|||||||
import { constants, createReadStream, createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
import { constants, createReadStream, createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { Writable } from 'node:stream';
|
import { Readable, Writable } from 'node:stream';
|
||||||
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
import { CrawlOptionsDto, WalkOptionsDto } from 'src/dtos/library.dto';
|
||||||
import {
|
|
||||||
DiskUsage,
|
|
||||||
IStorageRepository,
|
|
||||||
ImmichReadStream,
|
|
||||||
ImmichZipStream,
|
|
||||||
WatchEvents,
|
|
||||||
} from 'src/interfaces/storage.interface';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
|
|
||||||
|
export interface WatchEvents {
|
||||||
|
onReady(): void;
|
||||||
|
onAdd(path: string): void;
|
||||||
|
onChange(path: string): void;
|
||||||
|
onUnlink(path: string): void;
|
||||||
|
onError(error: Error): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImmichReadStream {
|
||||||
|
stream: Readable;
|
||||||
|
type?: string;
|
||||||
|
length?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImmichZipStream extends ImmichReadStream {
|
||||||
|
addFile: (inputPath: string, filename: string) => void;
|
||||||
|
finalize: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiskUsage {
|
||||||
|
available: number;
|
||||||
|
free: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StorageRepository implements IStorageRepository {
|
export class StorageRepository {
|
||||||
constructor(private logger: LoggingRepository) {
|
constructor(private logger: LoggingRepository) {
|
||||||
this.logger.setContext(StorageRepository.name);
|
this.logger.setContext(StorageRepository.name);
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { TagEntity } from 'src/entities/tag.entity';
|
import { TagEntity } from 'src/entities/tag.entity';
|
||||||
import { AssetTagItem, ITagRepository } from 'src/interfaces/tag.interface';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { DataSource, In, Repository } from 'typeorm';
|
import { DataSource, In, Repository } from 'typeorm';
|
||||||
|
|
||||||
|
export type AssetTagItem = { assetId: string; tagId: string };
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TagRepository implements ITagRepository {
|
export class TagRepository {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectDataSource() private dataSource: DataSource,
|
@InjectDataSource() private dataSource: DataSource,
|
||||||
@InjectRepository(TagEntity) private repository: Repository<TagEntity>,
|
@InjectRepository(TagEntity) private repository: Repository<TagEntity>,
|
||||||
|
@ -6,12 +6,6 @@ import { DummyValue, GenerateSql } from 'src/decorators';
|
|||||||
import { UserMetadata } from 'src/entities/user-metadata.entity';
|
import { UserMetadata } from 'src/entities/user-metadata.entity';
|
||||||
import { UserEntity, withMetadata } from 'src/entities/user.entity';
|
import { UserEntity, withMetadata } from 'src/entities/user.entity';
|
||||||
import { UserStatus } from 'src/enum';
|
import { UserStatus } from 'src/enum';
|
||||||
import {
|
|
||||||
IUserRepository,
|
|
||||||
UserFindOptions,
|
|
||||||
UserListFilter,
|
|
||||||
UserStatsQueryResponse,
|
|
||||||
} from 'src/interfaces/user.interface';
|
|
||||||
import { asUuid } from 'src/utils/database';
|
import { asUuid } from 'src/utils/database';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@ -34,8 +28,27 @@ const columns = [
|
|||||||
|
|
||||||
type Upsert = Insertable<DbUserMetadata>;
|
type Upsert = Insertable<DbUserMetadata>;
|
||||||
|
|
||||||
|
export interface UserListFilter {
|
||||||
|
withDeleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserStatsQueryResponse {
|
||||||
|
userId: string;
|
||||||
|
userName: string;
|
||||||
|
photos: number;
|
||||||
|
videos: number;
|
||||||
|
usage: number;
|
||||||
|
usagePhotos: number;
|
||||||
|
usageVideos: number;
|
||||||
|
quotaSizeInBytes: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserFindOptions {
|
||||||
|
withDeleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserRepository implements IUserRepository {
|
export class UserRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BOOLEAN] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.BOOLEAN] })
|
||||||
|
@ -16,7 +16,7 @@ import { AuthDto } from 'src/dtos/auth.dto';
|
|||||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
import { AlbumEntity } from 'src/entities/album.entity';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { AlbumAssetCount, AlbumInfoOptions } from 'src/interfaces/album.interface';
|
import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import { mapAsset } from 'src/dtos/asset-response.dto';
|
|||||||
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { AssetStatus, AssetType } from 'src/enum';
|
import { AssetStatus, AssetType } from 'src/enum';
|
||||||
import { AssetStats } from 'src/interfaces/asset.interface';
|
|
||||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
import { AssetStats } from 'src/repositories/asset.repository';
|
||||||
import { AssetService } from 'src/services/asset.service';
|
import { AssetService } from 'src/services/asset.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
|
@ -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 { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { 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,44 +6,43 @@ 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 { IAlbumRepository } from 'src/interfaces/album.interface';
|
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|
||||||
import { IDatabaseRepository } from 'src/interfaces/database.interface';
|
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IJobRepository } from 'src/interfaces/job.interface';
|
import { IJobRepository } from 'src/interfaces/job.interface';
|
||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
|
||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
|
||||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
|
||||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
|
||||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
|
||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
|
||||||
import { IUserRepository } from 'src/interfaces/user.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';
|
||||||
|
import { AlbumRepository } from 'src/repositories/album.repository';
|
||||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
|
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
|
||||||
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
import { AuditRepository } from 'src/repositories/audit.repository';
|
import { AuditRepository } from 'src/repositories/audit.repository';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
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 { LibraryRepository } from 'src/repositories/library.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.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';
|
||||||
import { MetadataRepository } from 'src/repositories/metadata.repository';
|
import { MetadataRepository } from 'src/repositories/metadata.repository';
|
||||||
|
import { MoveRepository } from 'src/repositories/move.repository';
|
||||||
import { NotificationRepository } from 'src/repositories/notification.repository';
|
import { NotificationRepository } from 'src/repositories/notification.repository';
|
||||||
import { OAuthRepository } from 'src/repositories/oauth.repository';
|
import { OAuthRepository } from 'src/repositories/oauth.repository';
|
||||||
|
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||||
|
import { PersonRepository } from 'src/repositories/person.repository';
|
||||||
import { ProcessRepository } from 'src/repositories/process.repository';
|
import { ProcessRepository } from 'src/repositories/process.repository';
|
||||||
|
import { SearchRepository } from 'src/repositories/search.repository';
|
||||||
import { ServerInfoRepository } from 'src/repositories/server-info.repository';
|
import { ServerInfoRepository } from 'src/repositories/server-info.repository';
|
||||||
import { SessionRepository } from 'src/repositories/session.repository';
|
import { SessionRepository } from 'src/repositories/session.repository';
|
||||||
|
import { SharedLinkRepository } from 'src/repositories/shared-link.repository';
|
||||||
|
import { StackRepository } from 'src/repositories/stack.repository';
|
||||||
|
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||||
|
import { TagRepository } from 'src/repositories/tag.repository';
|
||||||
import { TelemetryRepository } from 'src/repositories/telemetry.repository';
|
import { TelemetryRepository } from 'src/repositories/telemetry.repository';
|
||||||
import { TrashRepository } from 'src/repositories/trash.repository';
|
import { TrashRepository } from 'src/repositories/trash.repository';
|
||||||
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
|
import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
|
||||||
import { ViewRepository } from 'src/repositories/view-repository';
|
import { ViewRepository } from 'src/repositories/view-repository';
|
||||||
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
||||||
@ -57,39 +56,39 @@ export class BaseService {
|
|||||||
protected accessRepository: AccessRepository,
|
protected accessRepository: AccessRepository,
|
||||||
protected activityRepository: ActivityRepository,
|
protected activityRepository: ActivityRepository,
|
||||||
protected auditRepository: AuditRepository,
|
protected auditRepository: AuditRepository,
|
||||||
@Inject(IAlbumRepository) protected albumRepository: IAlbumRepository,
|
protected albumRepository: AlbumRepository,
|
||||||
protected albumUserRepository: AlbumUserRepository,
|
protected albumUserRepository: AlbumUserRepository,
|
||||||
@Inject(IAssetRepository) protected assetRepository: IAssetRepository,
|
protected assetRepository: AssetRepository,
|
||||||
protected configRepository: ConfigRepository,
|
protected configRepository: ConfigRepository,
|
||||||
protected cronRepository: CronRepository,
|
protected cronRepository: CronRepository,
|
||||||
@Inject(ICryptoRepository) protected cryptoRepository: CryptoRepository,
|
protected cryptoRepository: CryptoRepository,
|
||||||
@Inject(IDatabaseRepository) protected databaseRepository: IDatabaseRepository,
|
protected databaseRepository: DatabaseRepository,
|
||||||
@Inject(IEventRepository) protected eventRepository: IEventRepository,
|
@Inject(IEventRepository) protected eventRepository: IEventRepository,
|
||||||
@Inject(IJobRepository) protected jobRepository: IJobRepository,
|
@Inject(IJobRepository) protected jobRepository: IJobRepository,
|
||||||
protected keyRepository: ApiKeyRepository,
|
protected keyRepository: ApiKeyRepository,
|
||||||
@Inject(ILibraryRepository) protected libraryRepository: ILibraryRepository,
|
protected libraryRepository: LibraryRepository,
|
||||||
@Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository,
|
@Inject(IMachineLearningRepository) protected machineLearningRepository: IMachineLearningRepository,
|
||||||
protected mapRepository: MapRepository,
|
protected mapRepository: MapRepository,
|
||||||
protected mediaRepository: MediaRepository,
|
protected mediaRepository: MediaRepository,
|
||||||
protected memoryRepository: MemoryRepository,
|
protected memoryRepository: MemoryRepository,
|
||||||
protected metadataRepository: MetadataRepository,
|
protected metadataRepository: MetadataRepository,
|
||||||
@Inject(IMoveRepository) protected moveRepository: IMoveRepository,
|
protected moveRepository: MoveRepository,
|
||||||
protected notificationRepository: NotificationRepository,
|
protected notificationRepository: NotificationRepository,
|
||||||
protected oauthRepository: OAuthRepository,
|
protected oauthRepository: OAuthRepository,
|
||||||
@Inject(IPartnerRepository) protected partnerRepository: IPartnerRepository,
|
protected partnerRepository: PartnerRepository,
|
||||||
@Inject(IPersonRepository) protected personRepository: IPersonRepository,
|
protected personRepository: PersonRepository,
|
||||||
protected processRepository: ProcessRepository,
|
protected processRepository: ProcessRepository,
|
||||||
@Inject(ISearchRepository) protected searchRepository: ISearchRepository,
|
protected searchRepository: SearchRepository,
|
||||||
protected serverInfoRepository: ServerInfoRepository,
|
protected serverInfoRepository: ServerInfoRepository,
|
||||||
protected sessionRepository: SessionRepository,
|
protected sessionRepository: SessionRepository,
|
||||||
@Inject(ISharedLinkRepository) protected sharedLinkRepository: ISharedLinkRepository,
|
protected sharedLinkRepository: SharedLinkRepository,
|
||||||
@Inject(IStackRepository) protected stackRepository: IStackRepository,
|
protected stackRepository: StackRepository,
|
||||||
@Inject(IStorageRepository) protected storageRepository: IStorageRepository,
|
protected storageRepository: StorageRepository,
|
||||||
protected systemMetadataRepository: SystemMetadataRepository,
|
protected systemMetadataRepository: SystemMetadataRepository,
|
||||||
@Inject(ITagRepository) protected tagRepository: ITagRepository,
|
protected tagRepository: TagRepository,
|
||||||
protected telemetryRepository: TelemetryRepository,
|
protected telemetryRepository: TelemetryRepository,
|
||||||
protected trashRepository: TrashRepository,
|
protected trashRepository: TrashRepository,
|
||||||
@Inject(IUserRepository) protected userRepository: IUserRepository,
|
protected userRepository: UserRepository,
|
||||||
protected versionRepository: VersionHistoryRepository,
|
protected versionRepository: VersionHistoryRepository,
|
||||||
protected viewRepository: ViewRepository,
|
protected viewRepository: ViewRepository,
|
||||||
) {
|
) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { DatabaseExtension, EXTENSION_NAMES, VectorExtension } from 'src/interfaces/database.interface';
|
import { DatabaseExtension } from 'src/enum';
|
||||||
|
import { EXTENSION_NAMES, VectorExtension } from 'src/repositories/database.repository';
|
||||||
import { DatabaseService } from 'src/services/database.service';
|
import { DatabaseService } from 'src/services/database.service';
|
||||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
|
import { mockEnvData } from 'test/repositories/config.repository.mock';
|
||||||
import { newTestService, ServiceMocks } from 'test/utils';
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
@ -2,14 +2,9 @@ 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 {
|
import { DatabaseExtension } from 'src/enum';
|
||||||
DatabaseExtension,
|
|
||||||
DatabaseLock,
|
|
||||||
EXTENSION_NAMES,
|
|
||||||
VectorExtension,
|
|
||||||
VectorIndex,
|
|
||||||
} from 'src/interfaces/database.interface';
|
|
||||||
import { BootstrapEventPriority } from 'src/interfaces/event.interface';
|
import { BootstrapEventPriority } from 'src/interfaces/event.interface';
|
||||||
|
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';
|
||||||
|
|
||||||
type CreateFailedArgs = { name: string; extension: string; otherName: string };
|
type CreateFailedArgs = { name: string; extension: string; otherName: string };
|
||||||
|
@ -6,7 +6,7 @@ import { AuthDto } from 'src/dtos/auth.dto';
|
|||||||
import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto';
|
import { DownloadArchiveInfo, DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { ImmichReadStream } from 'src/interfaces/storage.interface';
|
import { ImmichReadStream } from 'src/repositories/storage.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { HumanReadableSize } from 'src/utils/bytes';
|
import { HumanReadableSize } from 'src/utils/bytes';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { DuplicateService } from 'src/services/duplicate.service';
|
import { DuplicateService } from 'src/services/duplicate.service';
|
||||||
import { SearchService } from 'src/services/search.service';
|
import { SearchService } from 'src/services/search.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
@ -4,9 +4,9 @@ import { mapAsset } from 'src/dtos/asset-response.dto';
|
|||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { WithoutProperty } from 'src/interfaces/asset.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 { AssetDuplicateResult } from 'src/interfaces/search.interface';
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
|
import { AssetDuplicateResult } from 'src/repositories/search.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 { isDuplicateDetectionEnabled } from 'src/utils/misc';
|
import { isDuplicateDetectionEnabled } from 'src/utils/misc';
|
||||||
|
@ -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 { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { 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';
|
||||||
|
@ -13,8 +13,8 @@ import {
|
|||||||
TranscodePolicy,
|
TranscodePolicy,
|
||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { JobCounts, JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobCounts, JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { MediaService } from 'src/services/media.service';
|
import { MediaService } from 'src/services/media.service';
|
||||||
import { RawImageInfo } from 'src/types';
|
import { RawImageInfo } from 'src/types';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
@ -18,7 +18,6 @@ import {
|
|||||||
VideoCodec,
|
VideoCodec,
|
||||||
VideoContainer,
|
VideoContainer,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { UpsertFileOptions, WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import {
|
import {
|
||||||
JOBS_ASSET_PAGINATION_SIZE,
|
JOBS_ASSET_PAGINATION_SIZE,
|
||||||
JobItem,
|
JobItem,
|
||||||
@ -27,6 +26,7 @@ import {
|
|||||||
JobStatus,
|
JobStatus,
|
||||||
QueueName,
|
QueueName,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
|
import { UpsertFileOptions, WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { AudioStreamInfo, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/types';
|
import { AudioStreamInfo, VideoFormat, VideoInterfaces, VideoStreamInfo } from 'src/types';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
|
@ -5,8 +5,8 @@ import { constants } from 'node:fs/promises';
|
|||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { ExifEntity } from 'src/entities/exif.entity';
|
import { ExifEntity } from 'src/entities/exif.entity';
|
||||||
import { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
|
import { AssetType, ExifOrientation, ImmichWorker, SourceType } from 'src/enum';
|
||||||
import { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { ImmichTags } from 'src/repositories/metadata.repository';
|
import { ImmichTags } from 'src/repositories/metadata.repository';
|
||||||
import { MetadataService } from 'src/services/metadata.service';
|
import { MetadataService } from 'src/services/metadata.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
@ -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 { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { DatabaseLock } from 'src/repositories/database.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';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { PartnerDirection } from 'src/interfaces/partner.interface';
|
import { PartnerDirection } from 'src/repositories/partner.repository';
|
||||||
import { PartnerService } from 'src/services/partner.service';
|
import { PartnerService } from 'src/services/partner.service';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
import { partnerStub } from 'test/fixtures/partner.stub';
|
import { partnerStub } from 'test/fixtures/partner.stub';
|
||||||
|
@ -4,7 +4,7 @@ import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos
|
|||||||
import { mapUser } from 'src/dtos/user.dto';
|
import { mapUser } from 'src/dtos/user.dto';
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { PartnerDirection, PartnerIds } from 'src/interfaces/partner.interface';
|
import { PartnerDirection, PartnerIds } from 'src/repositories/partner.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -3,10 +3,10 @@ import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto';
|
|||||||
import { mapFaces, mapPerson, PersonResponseDto } from 'src/dtos/person.dto';
|
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 { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
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 { DetectedFaces } from 'src/interfaces/machine-learning.interface';
|
||||||
import { FaceSearchResult } from 'src/interfaces/search.interface';
|
import { WithoutProperty } from 'src/repositories/asset.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';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
@ -32,7 +32,6 @@ import {
|
|||||||
SourceType,
|
SourceType,
|
||||||
SystemMetadataKey,
|
SystemMetadataKey,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import {
|
import {
|
||||||
JOBS_ASSET_PAGINATION_SIZE,
|
JOBS_ASSET_PAGINATION_SIZE,
|
||||||
JobItem,
|
JobItem,
|
||||||
@ -42,7 +41,8 @@ import {
|
|||||||
QueueName,
|
QueueName,
|
||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
import { BoundingBox } from 'src/interfaces/machine-learning.interface';
|
import { BoundingBox } from 'src/interfaces/machine-learning.interface';
|
||||||
import { UpdateFacesData } from 'src/interfaces/person.interface';
|
import { WithoutProperty } from 'src/repositories/asset.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';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
} from 'src/dtos/search.dto';
|
} from 'src/dtos/search.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { AssetOrder } from 'src/enum';
|
import { AssetOrder } from 'src/enum';
|
||||||
import { SearchExploreItem } from 'src/interfaces/search.interface';
|
import { SearchExploreItem } from 'src/repositories/search.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||||
import { isSmartSearchEnabled } from 'src/utils/misc';
|
import { isSmartSearchEnabled } from 'src/utils/misc';
|
||||||
@ -109,7 +109,7 @@ export class SearchService extends BaseService {
|
|||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSuggestions(userIds: string[], dto: SearchSuggestionRequestDto) {
|
private getSuggestions(userIds: string[], dto: SearchSuggestionRequestDto): Promise<Array<string | null>> {
|
||||||
switch (dto.type) {
|
switch (dto.type) {
|
||||||
case SearchSuggestionType.COUNTRY: {
|
case SearchSuggestionType.COUNTRY: {
|
||||||
return this.searchRepository.getCountries(userIds);
|
return this.searchRepository.getCountries(userIds);
|
||||||
@ -127,7 +127,7 @@ export class SearchService extends BaseService {
|
|||||||
return this.searchRepository.getCameraModels(userIds, dto);
|
return this.searchRepository.getCameraModels(userIds, dto);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return [] as (string | null)[];
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
UsageByUserDto,
|
UsageByUserDto,
|
||||||
} from 'src/dtos/server.dto';
|
} from 'src/dtos/server.dto';
|
||||||
import { StorageFolder, SystemMetadataKey } from 'src/enum';
|
import { StorageFolder, SystemMetadataKey } from 'src/enum';
|
||||||
import { UserStatsQueryResponse } from 'src/interfaces/user.interface';
|
import { UserStatsQueryResponse } from 'src/repositories/user.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { asHumanReadable } from 'src/utils/bytes';
|
import { asHumanReadable } from 'src/utils/bytes';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { SystemConfig } from 'src/config';
|
import { SystemConfig } from 'src/config';
|
||||||
import { ImmichWorker } from 'src/enum';
|
import { ImmichWorker } from 'src/enum';
|
||||||
import { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||||
|
import { WithoutProperty } from 'src/repositories/asset.repository';
|
||||||
import { SmartInfoService } from 'src/services/smart-info.service';
|
import { SmartInfoService } from 'src/services/smart-info.service';
|
||||||
import { getCLIPModelInfo } from 'src/utils/misc';
|
import { getCLIPModelInfo } from 'src/utils/misc';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
|
@ -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 { WithoutProperty } from 'src/interfaces/asset.interface';
|
|
||||||
import { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { DatabaseLock } from 'src/repositories/database.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 { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { 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,8 +4,8 @@ import { StorageCore } from 'src/cores/storage.core';
|
|||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { SystemFlags } from 'src/entities/system-metadata.entity';
|
import { SystemFlags } from 'src/entities/system-metadata.entity';
|
||||||
import { StorageFolder, SystemMetadataKey } from 'src/enum';
|
import { StorageFolder, SystemMetadataKey } from 'src/enum';
|
||||||
import { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { ImmichStartupError } from 'src/utils/misc';
|
import { ImmichStartupError } from 'src/utils/misc';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
import { TagEntity } from 'src/entities/tag.entity';
|
import { TagEntity } from 'src/entities/tag.entity';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { AssetTagItem } from 'src/interfaces/tag.interface';
|
import { AssetTagItem } from 'src/repositories/tag.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
import { upsertTags } from 'src/utils/tag';
|
import { upsertTags } from 'src/utils/tag';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { TimeBucketSize } from 'src/interfaces/asset.interface';
|
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
||||||
import { TimelineService } from 'src/services/timeline.service';
|
import { TimelineService } from 'src/services/timeline.service';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
|
@ -3,7 +3,7 @@ import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/
|
|||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { TimeBucketOptions } from 'src/interfaces/asset.interface';
|
import { TimeBucketOptions } from 'src/repositories/asset.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
} from 'src/dtos/user.dto';
|
} from 'src/dtos/user.dto';
|
||||||
import { UserMetadataKey, UserStatus } from 'src/enum';
|
import { UserMetadataKey, UserStatus } from 'src/enum';
|
||||||
import { JobName } from 'src/interfaces/job.interface';
|
import { JobName } from 'src/interfaces/job.interface';
|
||||||
import { UserFindOptions } from 'src/interfaces/user.interface';
|
import { UserFindOptions } from 'src/repositories/user.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
|
|||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { CacheControl, StorageFolder, UserMetadataKey } from 'src/enum';
|
import { CacheControl, StorageFolder, UserMetadataKey } from 'src/enum';
|
||||||
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
import { JobName, JobOf, JobStatus, QueueName } from 'src/interfaces/job.interface';
|
||||||
import { UserFindOptions } from 'src/interfaces/user.interface';
|
import { UserFindOptions } from 'src/repositories/user.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { ImmichFileResponse } from 'src/utils/file';
|
import { ImmichFileResponse } from 'src/utils/file';
|
||||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
||||||
|
@ -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 { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ArgOf } from 'src/interfaces/event.interface';
|
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 { 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,12 +5,12 @@ 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 { IAssetRepository } from 'src/interfaces/asset.interface';
|
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.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 { 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';
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ export const removeAssets = async (
|
|||||||
|
|
||||||
export type PartnerIdOptions = {
|
export type PartnerIdOptions = {
|
||||||
userId: string;
|
userId: string;
|
||||||
repository: IPartnerRepository;
|
repository: PartnerRepository;
|
||||||
/** only include partners with `inTimeline: true` */
|
/** only include partners with `inTimeline: true` */
|
||||||
timelineEnabled?: boolean;
|
timelineEnabled?: boolean;
|
||||||
};
|
};
|
||||||
@ -139,7 +139,7 @@ export const getMyPartnerIds = async ({ userId, repository, timelineEnabled }: P
|
|||||||
return [...partnerIds];
|
return [...partnerIds];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AssetHookRepositories = { asset: IAssetRepository; event: IEventRepository };
|
export type AssetHookRepositories = { asset: AssetRepository; event: IEventRepository };
|
||||||
|
|
||||||
export const onBeforeLink = async (
|
export const onBeforeLink = async (
|
||||||
{ asset: assetRepository, event: eventRepository }: AssetHookRepositories,
|
{ asset: assetRepository, event: eventRepository }: AssetHookRepositories,
|
||||||
|
@ -6,8 +6,8 @@ import * as _ from 'lodash';
|
|||||||
import { SystemConfig, defaults } from 'src/config';
|
import { SystemConfig, defaults } from 'src/config';
|
||||||
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
||||||
import { SystemMetadataKey } from 'src/enum';
|
import { SystemMetadataKey } from 'src/enum';
|
||||||
import { DatabaseLock } from 'src/interfaces/database.interface';
|
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
|
import { DatabaseLock } from 'src/repositories/database.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
|
||||||
import { DeepPartial } from 'src/types';
|
import { DeepPartial } from 'src/types';
|
||||||
|
@ -4,8 +4,8 @@ import { access, constants } from 'node:fs/promises';
|
|||||||
import { basename, extname, isAbsolute } from 'node:path';
|
import { basename, extname, isAbsolute } from 'node:path';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
import { CacheControl } from 'src/enum';
|
import { CacheControl } from 'src/enum';
|
||||||
import { ImmichReadStream } from 'src/interfaces/storage.interface';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { ImmichReadStream } from 'src/repositories/storage.repository';
|
||||||
import { isConnectionAborted } from 'src/utils/misc';
|
import { isConnectionAborted } from 'src/utils/misc';
|
||||||
|
|
||||||
export function getFileNameWithoutExtension(path: string): string {
|
export function getFileNameWithoutExtension(path: string): string {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { TagEntity } from 'src/entities/tag.entity';
|
import { TagEntity } from 'src/entities/tag.entity';
|
||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
import { TagRepository } from 'src/repositories/tag.repository';
|
||||||
|
|
||||||
type UpsertRequest = { userId: string; tags: string[] };
|
type UpsertRequest = { userId: string; tags: string[] };
|
||||||
export const upsertTags = async (repository: ITagRepository, { userId, tags }: UpsertRequest) => {
|
export const upsertTags = async (repository: TagRepository, { userId, tags }: UpsertRequest) => {
|
||||||
tags = [...new Set(tags)];
|
tags = [...new Set(tags)];
|
||||||
|
|
||||||
const results: TagEntity[] = [];
|
const results: TagEntity[] = [];
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
import { AlbumRepository } from 'src/repositories/album.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newAlbumRepositoryMock = (): Mocked<IAlbumRepository> => {
|
export const newAlbumRepositoryMock = (): Mocked<RepositoryInterface<AlbumRepository>> => {
|
||||||
return {
|
return {
|
||||||
getById: vitest.fn(),
|
getById: vitest.fn(),
|
||||||
getByAssetId: vitest.fn(),
|
getByAssetId: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newAssetRepositoryMock = (): Mocked<IAssetRepository> => {
|
export const newAssetRepositoryMock = (): Mocked<RepositoryInterface<AssetRepository>> => {
|
||||||
return {
|
return {
|
||||||
create: vitest.fn(),
|
create: vitest.fn(),
|
||||||
upsertExif: vitest.fn(),
|
upsertExif: vitest.fn(),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { ImmichEnvironment, ImmichWorker } from 'src/enum';
|
import { DatabaseExtension, ImmichEnvironment, ImmichWorker } from 'src/enum';
|
||||||
import { DatabaseExtension } from 'src/interfaces/database.interface';
|
|
||||||
import { ConfigRepository, EnvData } from 'src/repositories/config.repository';
|
import { ConfigRepository, EnvData } from 'src/repositories/config.repository';
|
||||||
import { RepositoryInterface } from 'src/types';
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newCryptoRepositoryMock = (): Mocked<ICryptoRepository> => {
|
export const newCryptoRepositoryMock = (): Mocked<RepositoryInterface<CryptoRepository>> => {
|
||||||
return {
|
return {
|
||||||
randomUUID: vitest.fn().mockReturnValue('random-uuid'),
|
randomUUID: vitest.fn().mockReturnValue('random-uuid'),
|
||||||
randomBytes: vitest.fn().mockReturnValue(Buffer.from('random-bytes', 'utf8')),
|
randomBytes: vitest.fn().mockReturnValue(Buffer.from('random-bytes', 'utf8')),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IDatabaseRepository } from 'src/interfaces/database.interface';
|
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newDatabaseRepositoryMock = (): Mocked<IDatabaseRepository> => {
|
export const newDatabaseRepositoryMock = (): Mocked<RepositoryInterface<DatabaseRepository>> => {
|
||||||
return {
|
return {
|
||||||
init: vitest.fn(),
|
init: vitest.fn(),
|
||||||
shutdown: vitest.fn(),
|
shutdown: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
import { LibraryRepository } from 'src/repositories/library.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newLibraryRepositoryMock = (): Mocked<ILibraryRepository> => {
|
export const newLibraryRepositoryMock = (): Mocked<RepositoryInterface<LibraryRepository>> => {
|
||||||
return {
|
return {
|
||||||
get: vitest.fn(),
|
get: vitest.fn(),
|
||||||
create: vitest.fn(),
|
create: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
import { MoveRepository } from 'src/repositories/move.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newMoveRepositoryMock = (): Mocked<IMoveRepository> => {
|
export const newMoveRepositoryMock = (): Mocked<RepositoryInterface<MoveRepository>> => {
|
||||||
return {
|
return {
|
||||||
create: vitest.fn(),
|
create: vitest.fn(),
|
||||||
getByEntity: vitest.fn(),
|
getByEntity: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newPartnerRepositoryMock = (): Mocked<IPartnerRepository> => {
|
export const newPartnerRepositoryMock = (): Mocked<RepositoryInterface<PartnerRepository>> => {
|
||||||
return {
|
return {
|
||||||
create: vitest.fn(),
|
create: vitest.fn(),
|
||||||
remove: vitest.fn(),
|
remove: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
import { PersonRepository } from 'src/repositories/person.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newPersonRepositoryMock = (): Mocked<IPersonRepository> => {
|
export const newPersonRepositoryMock = (): Mocked<RepositoryInterface<PersonRepository>> => {
|
||||||
return {
|
return {
|
||||||
getById: vitest.fn(),
|
getById: vitest.fn(),
|
||||||
getAll: vitest.fn(),
|
getAll: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
import { SearchRepository } from 'src/repositories/search.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newSearchRepositoryMock = (): Mocked<ISearchRepository> => {
|
export const newSearchRepositoryMock = (): Mocked<RepositoryInterface<SearchRepository>> => {
|
||||||
return {
|
return {
|
||||||
searchMetadata: vitest.fn(),
|
searchMetadata: vitest.fn(),
|
||||||
searchSmart: vitest.fn(),
|
searchSmart: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
import { SharedLinkRepository } from 'src/repositories/shared-link.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newSharedLinkRepositoryMock = (): Mocked<ISharedLinkRepository> => {
|
export const newSharedLinkRepositoryMock = (): Mocked<RepositoryInterface<SharedLinkRepository>> => {
|
||||||
return {
|
return {
|
||||||
getAll: vitest.fn(),
|
getAll: vitest.fn(),
|
||||||
get: vitest.fn(),
|
get: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
import { StackRepository } from 'src/repositories/stack.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newStackRepositoryMock = (): Mocked<IStackRepository> => {
|
export const newStackRepositoryMock = (): Mocked<RepositoryInterface<StackRepository>> => {
|
||||||
return {
|
return {
|
||||||
search: vitest.fn(),
|
search: vitest.fn(),
|
||||||
create: vitest.fn(),
|
create: vitest.fn(),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { WatchOptions } from 'chokidar';
|
import { WatchOptions } from 'chokidar';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { IStorageRepository, WatchEvents } from 'src/interfaces/storage.interface';
|
import { StorageRepository, WatchEvents } from 'src/repositories/storage.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
interface MockWatcherOptions {
|
interface MockWatcherOptions {
|
||||||
@ -39,7 +40,7 @@ export const makeMockWatcher =
|
|||||||
return () => Promise.resolve();
|
return () => Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const newStorageRepositoryMock = (reset = true): Mocked<IStorageRepository> => {
|
export const newStorageRepositoryMock = (reset = true): Mocked<RepositoryInterface<StorageRepository>> => {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
StorageCore.reset();
|
StorageCore.reset();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ITagRepository } from 'src/interfaces/tag.interface';
|
import { TagRepository } from 'src/repositories/tag.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newTagRepositoryMock = (): Mocked<ITagRepository> => {
|
export const newTagRepositoryMock = (): Mocked<RepositoryInterface<TagRepository>> => {
|
||||||
return {
|
return {
|
||||||
getAll: vitest.fn(),
|
getAll: vitest.fn(),
|
||||||
getByValue: vitest.fn(),
|
getByValue: vitest.fn(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
|
import { RepositoryInterface } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newUserRepositoryMock = (): Mocked<IUserRepository> => {
|
export const newUserRepositoryMock = (): Mocked<RepositoryInterface<UserRepository>> => {
|
||||||
return {
|
return {
|
||||||
get: vitest.fn(),
|
get: vitest.fn(),
|
||||||
getAdmin: vitest.fn(),
|
getAdmin: vitest.fn(),
|
||||||
|
@ -2,16 +2,14 @@ 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 { IAlbumRepository } from 'src/interfaces/album.interface';
|
|
||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.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';
|
||||||
|
import { AlbumRepository } from 'src/repositories/album.repository';
|
||||||
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
|
import { ApiKeyRepository } from 'src/repositories/api-key.repository';
|
||||||
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
import { AuditRepository } from 'src/repositories/audit.repository';
|
import { AuditRepository } from 'src/repositories/audit.repository';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { CronRepository } from 'src/repositories/cron.repository';
|
import { CronRepository } from 'src/repositories/cron.repository';
|
||||||
@ -40,6 +38,7 @@ import { SystemMetadataRepository } from 'src/repositories/system-metadata.repos
|
|||||||
import { TagRepository } from 'src/repositories/tag.repository';
|
import { TagRepository } from 'src/repositories/tag.repository';
|
||||||
import { TelemetryRepository } from 'src/repositories/telemetry.repository';
|
import { TelemetryRepository } from 'src/repositories/telemetry.repository';
|
||||||
import { TrashRepository } from 'src/repositories/trash.repository';
|
import { TrashRepository } from 'src/repositories/trash.repository';
|
||||||
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
|
import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
|
||||||
import { ViewRepository } from 'src/repositories/view-repository';
|
import { ViewRepository } from 'src/repositories/view-repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
@ -100,14 +99,14 @@ type IAccessRepository = { [K in keyof AccessRepository]: RepositoryInterface<Ac
|
|||||||
export type ServiceMocks = {
|
export type ServiceMocks = {
|
||||||
access: IAccessRepositoryMock;
|
access: IAccessRepositoryMock;
|
||||||
activity: Mocked<RepositoryInterface<ActivityRepository>>;
|
activity: Mocked<RepositoryInterface<ActivityRepository>>;
|
||||||
album: Mocked<IAlbumRepository>;
|
album: Mocked<RepositoryInterface<AlbumRepository>>;
|
||||||
albumUser: Mocked<RepositoryInterface<AlbumUserRepository>>;
|
albumUser: Mocked<RepositoryInterface<AlbumUserRepository>>;
|
||||||
apiKey: Mocked<RepositoryInterface<ApiKeyRepository>>;
|
apiKey: Mocked<RepositoryInterface<ApiKeyRepository>>;
|
||||||
audit: Mocked<RepositoryInterface<AuditRepository>>;
|
audit: Mocked<RepositoryInterface<AuditRepository>>;
|
||||||
asset: Mocked<IAssetRepository>;
|
asset: Mocked<RepositoryInterface<AssetRepository>>;
|
||||||
config: Mocked<RepositoryInterface<ConfigRepository>>;
|
config: Mocked<RepositoryInterface<ConfigRepository>>;
|
||||||
cron: Mocked<RepositoryInterface<CronRepository>>;
|
cron: Mocked<RepositoryInterface<CronRepository>>;
|
||||||
crypto: Mocked<ICryptoRepository>;
|
crypto: Mocked<RepositoryInterface<CryptoRepository>>;
|
||||||
database: Mocked<RepositoryInterface<DatabaseRepository>>;
|
database: Mocked<RepositoryInterface<DatabaseRepository>>;
|
||||||
event: Mocked<IEventRepository>;
|
event: Mocked<IEventRepository>;
|
||||||
job: Mocked<RepositoryInterface<JobRepository>>;
|
job: Mocked<RepositoryInterface<JobRepository>>;
|
||||||
@ -134,7 +133,7 @@ export type ServiceMocks = {
|
|||||||
tag: Mocked<RepositoryInterface<TagRepository>>;
|
tag: Mocked<RepositoryInterface<TagRepository>>;
|
||||||
telemetry: ITelemetryRepositoryMock;
|
telemetry: ITelemetryRepositoryMock;
|
||||||
trash: Mocked<RepositoryInterface<TrashRepository>>;
|
trash: Mocked<RepositoryInterface<TrashRepository>>;
|
||||||
user: Mocked<IUserRepository>;
|
user: Mocked<RepositoryInterface<UserRepository>>;
|
||||||
versionHistory: Mocked<RepositoryInterface<VersionHistoryRepository>>;
|
versionHistory: Mocked<RepositoryInterface<VersionHistoryRepository>>;
|
||||||
view: Mocked<RepositoryInterface<ViewRepository>>;
|
view: Mocked<RepositoryInterface<ViewRepository>>;
|
||||||
};
|
};
|
||||||
@ -192,39 +191,39 @@ export const newTestService = <T extends BaseService>(
|
|||||||
accessMock as IAccessRepository as AccessRepository,
|
accessMock as IAccessRepository as AccessRepository,
|
||||||
activityMock as RepositoryInterface<ActivityRepository> as ActivityRepository,
|
activityMock as RepositoryInterface<ActivityRepository> as ActivityRepository,
|
||||||
auditMock as RepositoryInterface<AuditRepository> as AuditRepository,
|
auditMock as RepositoryInterface<AuditRepository> as AuditRepository,
|
||||||
albumMock,
|
albumMock as RepositoryInterface<AlbumRepository> as AlbumRepository,
|
||||||
albumUserMock as RepositoryInterface<AlbumUserRepository> as AlbumUserRepository,
|
albumUserMock as RepositoryInterface<AlbumUserRepository> as AlbumUserRepository,
|
||||||
assetMock,
|
assetMock as RepositoryInterface<AssetRepository> as AssetRepository,
|
||||||
configMock,
|
configMock,
|
||||||
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,
|
databaseMock as RepositoryInterface<DatabaseRepository> as DatabaseRepository,
|
||||||
eventMock,
|
eventMock,
|
||||||
jobMock,
|
jobMock,
|
||||||
apiKeyMock as RepositoryInterface<ApiKeyRepository> as ApiKeyRepository,
|
apiKeyMock as RepositoryInterface<ApiKeyRepository> as ApiKeyRepository,
|
||||||
libraryMock,
|
libraryMock as RepositoryInterface<LibraryRepository> as LibraryRepository,
|
||||||
machineLearningMock,
|
machineLearningMock,
|
||||||
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,
|
||||||
metadataMock as RepositoryInterface<MetadataRepository> as MetadataRepository,
|
metadataMock as RepositoryInterface<MetadataRepository> as MetadataRepository,
|
||||||
moveMock,
|
moveMock as RepositoryInterface<MoveRepository> as MoveRepository,
|
||||||
notificationMock as RepositoryInterface<NotificationRepository> as NotificationRepository,
|
notificationMock as RepositoryInterface<NotificationRepository> as NotificationRepository,
|
||||||
oauthMock as RepositoryInterface<OAuthRepository> as OAuthRepository,
|
oauthMock as RepositoryInterface<OAuthRepository> as OAuthRepository,
|
||||||
partnerMock,
|
partnerMock as RepositoryInterface<PartnerRepository> as PartnerRepository,
|
||||||
personMock,
|
personMock as RepositoryInterface<PersonRepository> as PersonRepository,
|
||||||
processMock as RepositoryInterface<ProcessRepository> as ProcessRepository,
|
processMock as RepositoryInterface<ProcessRepository> as ProcessRepository,
|
||||||
searchMock,
|
searchMock as RepositoryInterface<SearchRepository> as SearchRepository,
|
||||||
serverInfoMock as RepositoryInterface<ServerInfoRepository> as ServerInfoRepository,
|
serverInfoMock as RepositoryInterface<ServerInfoRepository> as ServerInfoRepository,
|
||||||
sessionMock as RepositoryInterface<SessionRepository> as SessionRepository,
|
sessionMock as RepositoryInterface<SessionRepository> as SessionRepository,
|
||||||
sharedLinkMock,
|
sharedLinkMock as RepositoryInterface<SharedLinkRepository> as SharedLinkRepository,
|
||||||
stackMock,
|
stackMock as RepositoryInterface<StackRepository> as StackRepository,
|
||||||
storageMock,
|
storageMock as RepositoryInterface<StorageRepository> as StorageRepository,
|
||||||
systemMock as RepositoryInterface<SystemMetadataRepository> as SystemMetadataRepository,
|
systemMock as RepositoryInterface<SystemMetadataRepository> as SystemMetadataRepository,
|
||||||
tagMock,
|
tagMock as RepositoryInterface<TagRepository> as TagRepository,
|
||||||
telemetryMock as unknown as TelemetryRepository,
|
telemetryMock as unknown as TelemetryRepository,
|
||||||
trashMock as RepositoryInterface<TrashRepository> as TrashRepository,
|
trashMock as RepositoryInterface<TrashRepository> as TrashRepository,
|
||||||
userMock,
|
userMock as RepositoryInterface<UserRepository> as UserRepository,
|
||||||
versionHistoryMock as RepositoryInterface<VersionHistoryRepository> as VersionHistoryRepository,
|
versionHistoryMock as RepositoryInterface<VersionHistoryRepository> as VersionHistoryRepository,
|
||||||
viewMock as RepositoryInterface<ViewRepository> as ViewRepository,
|
viewMock as RepositoryInterface<ViewRepository> as ViewRepository,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user