import { ILibraryRepository, LibraryStatsResponseDto } from '@app/domain'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { IsNull, Not } from 'typeorm'; import { Repository } from 'typeorm/repository/Repository'; import { LibraryEntity, LibraryType } from '../entities'; @Injectable() export class LibraryRepository implements ILibraryRepository { constructor(@InjectRepository(LibraryEntity) private repository: Repository) {} get(id: string, withDeleted = false): Promise { return this.repository.findOneOrFail({ where: { id, }, relations: { owner: true }, withDeleted, }); } existsByName(name: string, withDeleted = false): Promise { return this.repository.exist({ where: { name, }, withDeleted, }); } getCountForUser(ownerId: string): Promise { return this.repository.countBy({ ownerId }); } getDefaultUploadLibrary(ownerId: string): Promise { return this.repository.findOne({ where: { ownerId: ownerId, type: LibraryType.UPLOAD, }, order: { createdAt: 'ASC', }, }); } getUploadLibraryCount(ownerId: string): Promise { return this.repository.count({ where: { ownerId: ownerId, type: LibraryType.UPLOAD, }, }); } getAllByUserId(ownerId: string, type?: LibraryType): Promise { return this.repository.find({ where: { ownerId, isVisible: true, type, }, relations: { owner: true, }, order: { createdAt: 'ASC', }, }); } getAll(withDeleted = false, type?: LibraryType): Promise { return this.repository.find({ where: { type }, relations: { owner: true, }, order: { createdAt: 'ASC', }, withDeleted, }); } getAllDeleted(): Promise { return this.repository.find({ where: { isVisible: true, deletedAt: Not(IsNull()), }, relations: { owner: true, }, order: { createdAt: 'ASC', }, withDeleted: true, }); } create(library: Omit): Promise { return this.repository.save(library); } async delete(id: string): Promise { await this.repository.delete({ id }); } async softDelete(id: string): Promise { await this.repository.softDelete({ id }); } async update(library: Partial): Promise { return this.save(library); } async getStatistics(id: string): Promise { const stats = await this.repository .createQueryBuilder('libraries') .addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'IMAGE' AND assets.isVisible)`, 'photos') .addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'VIDEO' AND assets.isVisible)`, 'videos') .addSelect('COALESCE(SUM(exif.fileSizeInByte), 0)', 'usage') .leftJoin('libraries.assets', 'assets') .leftJoin('assets.exifInfo', 'exif') .groupBy('libraries.id') .where('libraries.id = :id', { id }) .getRawOne(); return { photos: Number(stats.photos), videos: Number(stats.videos), usage: Number(stats.usage), total: Number(stats.photos) + Number(stats.videos), }; } async getOnlineAssetPaths(libraryId: string): Promise { // Return all non-offline asset paths for a given library const rawResults = await this.repository .createQueryBuilder('library') .innerJoinAndSelect('library.assets', 'assets') .where('library.id = :id', { id: libraryId }) .andWhere('assets.isOffline = false') .select('assets.originalPath') .getRawMany(); const results: string[] = []; for (const rawPath of rawResults) { results.push(rawPath.assets_originalPath); } return results; } async getAssetIds(libraryId: string, withDeleted = false): Promise { let query = await this.repository .createQueryBuilder('library') .innerJoinAndSelect('library.assets', 'assets') .where('library.id = :id', { id: libraryId }) .select('assets.id'); if (withDeleted) { query = query.withDeleted(); } // Return all asset paths for a given library const rawResults = await query.getRawMany(); const results: string[] = []; for (const rawPath of rawResults) { results.push(rawPath.assets_id); } return results; } private async save(library: Partial) { const { id } = await this.repository.save(library); return this.repository.findOneByOrFail({ id }); } }