diff --git a/backend/src/collections/imagedb/imagedb.service.ts b/backend/src/collections/imagedb/imagedb.service.ts index d7e67b1..27745db 100644 --- a/backend/src/collections/imagedb/imagedb.service.ts +++ b/backend/src/collections/imagedb/imagedb.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import Crypto from 'crypto'; import { AsyncFailable, - Fail, HasSuccess + Fail } from 'picsur-shared/dist/types'; import { Repository } from 'typeorm'; import { EImageBackend } from '../../models/entities/image.entity'; @@ -20,14 +19,9 @@ export class ImageDBService { image: Buffer, type: string, ): AsyncFailable { - const hash = this.hash(image); - const find = await this.findOne(hash); - if (HasSuccess(find)) return find; - let imageEntity = new EImageBackend(); imageEntity.data = image; imageEntity.mime = type; - imageEntity.hash = hash; try { imageEntity = await this.imageRepository.save(imageEntity); @@ -39,14 +33,14 @@ export class ImageDBService { } public async findOne( - hash: string, + id: string, getPrivate?: B, ): AsyncFailable< B extends undefined ? EImageBackend : Required > { try { const found = await this.imageRepository.findOne({ - where: { hash }, + where: { id }, select: getPrivate ? GetCols(this.imageRepository) : undefined, }); @@ -79,9 +73,9 @@ export class ImageDBService { } } - public async delete(hash: string): AsyncFailable { + public async delete(id: string): AsyncFailable { try { - const result = await this.imageRepository.delete({ hash }); + const result = await this.imageRepository.delete({ id }); if (result.affected === 0) return Fail('Image not found'); } catch (e: any) { return Fail(e?.message); @@ -100,8 +94,4 @@ export class ImageDBService { } return true; } - - private hash(image: Buffer): string { - return Crypto.createHash('sha256').update(image).digest('hex'); - } } diff --git a/backend/src/managers/imagemanager/imagemanager.service.ts b/backend/src/managers/imagemanager/imagemanager.service.ts index 02d7477..e1bb10b 100644 --- a/backend/src/managers/imagemanager/imagemanager.service.ts +++ b/backend/src/managers/imagemanager/imagemanager.service.ts @@ -19,16 +19,16 @@ export class ImageManagerService { private readonly processService: ImageProcessorService, ) {} - public async retrieveInfo(hash: string): AsyncFailable { - return await this.imagesService.findOne(hash); + public async retrieveInfo(id: string): AsyncFailable { + return await this.imagesService.findOne(id); } // Image data buffer is not included by default, this also returns that buffer // Dont send to client, keep in backend public async retrieveComplete( - hash: string, + id: string, ): AsyncFailable> { - return await this.imagesService.findOne(hash, true); + return await this.imagesService.findOne(id, true); } public async upload( diff --git a/backend/src/models/entities/image.entity.ts b/backend/src/models/entities/image.entity.ts index 21198c5..c022392 100644 --- a/backend/src/models/entities/image.entity.ts +++ b/backend/src/models/entities/image.entity.ts @@ -1,5 +1,5 @@ import { EImageSchema } from 'picsur-shared/dist/entities/image.entity'; -import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; import { z } from 'zod'; const OverriddenEImageSchema = EImageSchema.omit({ data: true }).merge( @@ -12,11 +12,7 @@ type OverriddenEImage = z.infer; @Entity() export class EImageBackend implements OverriddenEImage { @PrimaryGeneratedColumn('uuid') - id?: string; - - @Index() - @Column({ unique: true, nullable: false }) - hash: string; + id: string; @Column({ nullable: false }) mime: string; @@ -24,4 +20,7 @@ export class EImageBackend implements OverriddenEImage { // Binary data @Column({ type: 'bytea', nullable: false, select: false }) data?: Buffer; + + @Column({ type: 'bytea', nullable: true, select: false }) + originaldata?: Buffer; } diff --git a/backend/src/routes/image/imageid.validator.ts b/backend/src/routes/image/imageid.validator.ts index 5af7ce1..d2e5974 100644 --- a/backend/src/routes/image/imageid.validator.ts +++ b/backend/src/routes/image/imageid.validator.ts @@ -4,13 +4,12 @@ import { Injectable, PipeTransform } from '@nestjs/common'; -import { SHA256Regex } from 'picsur-shared/dist/util/common-regex'; +import { UUIDRegex } from 'picsur-shared/dist/util/common-regex'; @Injectable() export class ImageIdValidator implements PipeTransform { transform(value: string, metadata: ArgumentMetadata): string { - // Check regex for sha256 - if (SHA256Regex.test(value)) return value; + if (UUIDRegex.test(value)) return value; throw new BadRequestException('Invalid image id'); } } diff --git a/backend/src/routes/image/imageroute.controller.ts b/backend/src/routes/image/imageroute.controller.ts index b7d92fa..5c09081 100644 --- a/backend/src/routes/image/imageroute.controller.ts +++ b/backend/src/routes/image/imageroute.controller.ts @@ -30,14 +30,14 @@ export class ImageController { constructor(private readonly imagesService: ImageManagerService) {} - @Get(':hash') + @Get(':id') async getImage( // Usually passthrough is for manually sending the response, // But we need it here to set the mime type @Res({ passthrough: true }) res: FastifyReply, - @Param('hash', ImageIdValidator) hash: string, + @Param('id', ImageIdValidator) id: string, ): Promise { - const image = await this.imagesService.retrieveComplete(hash); + const image = await this.imagesService.retrieveComplete(id); if (HasFailed(image)) { this.logger.warn(image.getReason()); throw new NotFoundException('Could not find image'); @@ -47,12 +47,12 @@ export class ImageController { return image.data; } - @Head(':hash') + @Head(':id') async headImage( @Res({ passthrough: true }) res: FastifyReply, - @Param('hash', ImageIdValidator) hash: string, + @Param('id', ImageIdValidator) id: string, ) { - const image = await this.imagesService.retrieveInfo(hash); + const image = await this.imagesService.retrieveInfo(id); if (HasFailed(image)) { this.logger.warn(image.getReason()); throw new NotFoundException('Could not find image'); @@ -61,12 +61,12 @@ export class ImageController { res.type(image.mime); } - @Get('meta/:hash') + @Get('meta/:id') @Returns(ImageMetaResponse) async getImageMeta( - @Param('hash', ImageIdValidator) hash: string, + @Param('id', ImageIdValidator) id: string, ): Promise { - const image = await this.imagesService.retrieveInfo(hash); + const image = await this.imagesService.retrieveInfo(id); if (HasFailed(image)) { this.logger.warn(image.getReason()); throw new NotFoundException('Could not find image'); diff --git a/frontend/src/app/routes/processing/processing.component.ts b/frontend/src/app/routes/processing/processing.component.ts index e2e34c3..a805ece 100644 --- a/frontend/src/app/routes/processing/processing.component.ts +++ b/frontend/src/app/routes/processing/processing.component.ts @@ -23,11 +23,11 @@ export class ProcessingComponent implements OnInit { history.replaceState(null, ''); - const hash = await this.imageService.UploadImage(state.imageFile); - if (HasFailed(hash)) { - return this.utilService.quitError(hash.getReason()); + const id = await this.imageService.UploadImage(state.imageFile); + if (HasFailed(id)) { + return this.utilService.quitError(id.getReason()); } - this.router.navigate([`/view/`, hash], { replaceUrl: true }); + this.router.navigate([`/view/`, id], { replaceUrl: true }); } } diff --git a/frontend/src/app/routes/view/view.component.ts b/frontend/src/app/routes/view/view.component.ts index a5a0969..f113d2f 100644 --- a/frontend/src/app/routes/view/view.component.ts +++ b/frontend/src/app/routes/view/view.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { ImageLinks } from 'picsur-shared/dist/dto/imagelinks.dto'; import { HasFailed } from 'picsur-shared/dist/types'; -import { SHA256Regex } from 'picsur-shared/dist/util/common-regex'; +import { UUIDRegex } from 'picsur-shared/dist/util/common-regex'; import { ImageService } from 'src/app/services/api/image.service'; import { UtilService } from 'src/app/util/util.service'; @@ -22,17 +22,17 @@ export class ViewComponent implements OnInit { async ngOnInit() { const params = this.route.snapshot.paramMap; - const hash = params.get('hash') ?? ''; - if (!SHA256Regex.test(hash)) { + const id = params.get('id') ?? ''; + if (!UUIDRegex.test(id)) { return this.utilService.quitError('Invalid image link'); } - const metadata = await this.imageService.GetImageMeta(hash); + const metadata = await this.imageService.GetImageMeta(id); if (HasFailed(metadata)) { return this.utilService.quitError(metadata.getReason()); } - this.imageLinks = this.imageService.CreateImageLinksFromID(hash); + this.imageLinks = this.imageService.CreateImageLinksFromID(id); } downloadImage() { diff --git a/frontend/src/app/routes/view/view.routing.module.ts b/frontend/src/app/routes/view/view.routing.module.ts index 0b1b2ba..469fecc 100644 --- a/frontend/src/app/routes/view/view.routing.module.ts +++ b/frontend/src/app/routes/view/view.routing.module.ts @@ -7,7 +7,7 @@ import { ViewComponent } from './view.component'; const routes: PRoutes = [ { - path: ':hash', + path: ':id', component: ViewComponent, canActivate: [PermissionGuard], data: { permissions: [Permission.ImageView] }, diff --git a/frontend/src/app/services/api/image.service.ts b/frontend/src/app/services/api/image.service.ts index 2dae7e0..52a49c0 100644 --- a/frontend/src/app/services/api/image.service.ts +++ b/frontend/src/app/services/api/image.service.ts @@ -19,7 +19,7 @@ export class ImageService { new ImageUploadRequest(image) ); - return Open(result, 'hash'); + return Open(result, 'id'); } public async GetImageMeta(image: string): AsyncFailable { diff --git a/shared/src/entities/image.entity.ts b/shared/src/entities/image.entity.ts index c0ca60c..d39092e 100644 --- a/shared/src/entities/image.entity.ts +++ b/shared/src/entities/image.entity.ts @@ -1,10 +1,8 @@ import { z } from 'zod'; -import { SHA256Regex } from '../util/common-regex'; import { IsEntityID } from '../validators/entity-id.validator'; export const EImageSchema = z.object({ - id: IsEntityID().optional(), - hash: z.string().regex(SHA256Regex), + id: IsEntityID(), data: z.undefined(), mime: z.string(), });