diff --git a/backend/src/collections/image-db/image-db.service.ts b/backend/src/collections/image-db/image-db.service.ts index 864da50..eb4a95c 100644 --- a/backend/src/collections/image-db/image-db.service.ts +++ b/backend/src/collections/image-db/image-db.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AsyncFailable, Fail } from 'picsur-shared/dist/types'; -import { Repository } from 'typeorm'; +import { In, Repository } from 'typeorm'; import { EImageDerivativeBackend } from '../../models/entities/image-derivative.entity'; import { EImageFileBackend } from '../../models/entities/image-file.entity'; import { EImageBackend } from '../../models/entities/image.entity'; @@ -33,10 +33,13 @@ export class ImageDBService { return imageEntity; } - public async findOne(id: string): AsyncFailable { + public async findOne( + id: string, + userid: string | undefined, + ): AsyncFailable { try { const found = await this.imageRepo.findOne({ - where: { id }, + where: { id, user_id: userid }, }); if (!found) return Fail('Image not found'); @@ -49,7 +52,7 @@ export class ImageDBService { public async findMany( count: number, page: number, - userid: string | false, + userid: string | undefined, ): AsyncFailable { if (count < 1 || page < 0) return Fail('Invalid page'); if (count > 100) return Fail('Too many results'); @@ -59,7 +62,7 @@ export class ImageDBService { skip: count * page, take: count, where: { - user_id: userid === false ? undefined : userid, + user_id: userid, }, }); @@ -70,15 +73,40 @@ export class ImageDBService { } } - public async delete(id: string): AsyncFailable { + public async findList( + ids: string[], + userid: string | undefined, + ): AsyncFailable { + if (ids.length === 0) return []; + if (ids.length > 500) return Fail('Too many results'); + + try { + const found = await this.imageRepo.find({ + where: { + id: In(ids), + user_id: userid, + }, + }); + + if (found === undefined) return Fail('Images not found'); + return found; + } catch (e) { + return Fail(e); + } + } + + public async delete(ids: string[]): AsyncFailable { + if (ids.length === 0) return true; + if (ids.length > 500) return Fail('Too many results'); + try { const derivativesResult = await this.imageDerivativeRepo.delete({ - image_id: id, + image_id: In(ids), }); const filesResult = await this.imageFileRepo.delete({ - image_id: id, + image_id: In(ids), }); - const result = await this.imageRepo.delete({ id }); + const result = await this.imageRepo.delete({ id: In(ids) }); if ( result.affected === 0 && diff --git a/backend/src/managers/image/image.service.ts b/backend/src/managers/image/image.service.ts index 2802609..df50b74 100644 --- a/backend/src/managers/image/image.service.ts +++ b/backend/src/managers/image/image.service.ts @@ -33,18 +33,35 @@ export class ImageManagerService { private readonly sysPref: SysPreferenceService, ) {} - public async findOne(id: string): AsyncFailable { - return await this.imagesService.findOne(id); + public async findOne( + id: string, + ): AsyncFailable { + return await this.imagesService.findOne(id, undefined); } public async findMany( count: number, page: number, - userid: string | false, + userid: string | undefined, ): AsyncFailable { return await this.imagesService.findMany(count, page, userid); } + public async deleteMany( + ids: string[], + userid: string | undefined, + ): AsyncFailable { + const images = await this.imagesService.findList(ids, userid); + if (HasFailed(images)) return images; + + const availableIds = images.map(image => image.id); + + const deleteResult = await this.imagesService.delete(availableIds); + if (HasFailed(deleteResult)) return deleteResult; + + return images; + } + public async upload( image: Buffer, userid: string, diff --git a/backend/src/routes/image/image-manage.controller.ts b/backend/src/routes/image/image-manage.controller.ts index 9baa050..092c5c4 100644 --- a/backend/src/routes/image/image-manage.controller.ts +++ b/backend/src/routes/image/image-manage.controller.ts @@ -1,4 +1,5 @@ import { + BadRequestException, Body, Controller, InternalServerErrorException, @@ -6,6 +7,8 @@ import { Post } from '@nestjs/common'; import { + ImageDeleteRequest, + ImageDeleteResponse, ImageListRequest, ImageListResponse, ImageUploadResponse @@ -61,7 +64,7 @@ export class ImageManageController { const images = await this.imagesService.findMany( body.count, body.page, - userid, + body.user_id, ); if (HasFailed(images)) { this.logger.warn(images.getReason()); @@ -74,4 +77,26 @@ export class ImageManageController { page: body.page, }; } + + @Post('delete') + @Returns(ImageDeleteResponse) + async deleteImage( + @Body() body: ImageDeleteRequest, + @ReqUserID() userid: string, + @HasPermission(Permission.ImageAdmin) isImageAdmin: boolean, + ): Promise { + const deletedImages = await this.imagesService.deleteMany( + body.ids, + isImageAdmin ? undefined : userid, + ); + if (HasFailed(deletedImages)) { + this.logger.warn(deletedImages.getReason()); + throw new BadRequestException('Could not delete images'); + } + + return { + images: deletedImages, + count: deletedImages.length, + }; + } } diff --git a/shared/src/dto/api/image-manage.dto.ts b/shared/src/dto/api/image-manage.dto.ts index f49aa78..40b8868 100644 --- a/shared/src/dto/api/image-manage.dto.ts +++ b/shared/src/dto/api/image-manage.dto.ts @@ -16,9 +16,7 @@ export const ImageListRequestSchema = z.object({ page: IsPosInt(), user_id: z.string().uuid().optional(), }); -export class ImageListRequest extends createZodDto( - ImageListRequestSchema, -) {} +export class ImageListRequest extends createZodDto(ImageListRequestSchema) {} export const ImageListResponseSchema = z.object({ images: z.array(EImageSchema), @@ -26,3 +24,20 @@ export const ImageListResponseSchema = z.object({ page: IsPosInt(), }); export class ImageListResponse extends createZodDto(ImageListResponseSchema) {} + +// Image Delete + +export const ImageDeleteRequestSchema = z.object({ + ids: z.array(z.string().uuid()), +}); +export class ImageDeleteRequest extends createZodDto( + ImageDeleteRequestSchema, +) {} + +export const ImageDeleteResponseSchema = z.object({ + images: z.array(EImageSchema), + count: IsPosInt(), +}); +export class ImageDeleteResponse extends createZodDto( + ImageDeleteResponseSchema, +) {}