304 lines
8.1 KiB
TypeScript
304 lines
8.1 KiB
TypeScript
![]() |
import {
|
||
|
Controller,
|
||
|
Get,
|
||
|
Put,
|
||
|
Param,
|
||
|
Body,
|
||
|
UseGuards,
|
||
|
Request,
|
||
|
HttpStatus,
|
||
|
BadRequestException,
|
||
|
ForbiddenException,
|
||
|
NotFoundException,
|
||
|
} from '@nestjs/common';
|
||
|
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
|
||
|
import { JwtAuthGuard } from '../auth/auth.guard';
|
||
|
import { ImagesService } from './images.service';
|
||
|
import { UpdateFilenameDto, UpdateFilenameResponseDto } from './dto/update-filename.dto';
|
||
|
import { ImageResponseDto, BatchImagesResponseDto } from './dto/image-response.dto';
|
||
|
|
||
|
@ApiTags('images')
|
||
|
@Controller('api/image')
|
||
|
@UseGuards(JwtAuthGuard)
|
||
|
@ApiBearerAuth()
|
||
|
export class ImagesController {
|
||
|
constructor(private readonly imagesService: ImagesService) {}
|
||
|
|
||
|
@Put(':imageId/filename')
|
||
|
@ApiOperation({
|
||
|
summary: 'Update image filename',
|
||
|
description: 'Updates the proposed filename for a specific image',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Filename updated successfully',
|
||
|
type: UpdateFilenameResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.BAD_REQUEST,
|
||
|
description: 'Invalid filename or request data',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Image not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to update this image',
|
||
|
})
|
||
|
async updateImageFilename(
|
||
|
@Param('imageId') imageId: string,
|
||
|
@Body() updateFilenameDto: UpdateFilenameDto,
|
||
|
@Request() req: any,
|
||
|
): Promise<UpdateFilenameResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const result = await this.imagesService.updateFilename(
|
||
|
imageId,
|
||
|
userId,
|
||
|
updateFilenameDto.new_name
|
||
|
);
|
||
|
|
||
|
return result;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to update image filename');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get(':imageId')
|
||
|
@ApiOperation({
|
||
|
summary: 'Get image details',
|
||
|
description: 'Returns detailed information about a specific image',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Image details retrieved successfully',
|
||
|
type: ImageResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Image not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to access this image',
|
||
|
})
|
||
|
async getImage(
|
||
|
@Param('imageId') imageId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<ImageResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const image = await this.imagesService.getImage(imageId, userId);
|
||
|
return image;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to get image details');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get('batch/:batchId')
|
||
|
@ApiOperation({
|
||
|
summary: 'Get all images in a batch',
|
||
|
description: 'Returns all images belonging to a specific batch',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batch images retrieved successfully',
|
||
|
type: BatchImagesResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Batch not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to access this batch',
|
||
|
})
|
||
|
async getBatchImages(
|
||
|
@Param('batchId') batchId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<BatchImagesResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const batchImages = await this.imagesService.getBatchImages(batchId, userId);
|
||
|
return batchImages;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to get batch images');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get(':imageId/download')
|
||
|
@ApiOperation({
|
||
|
summary: 'Get image download URL',
|
||
|
description: 'Returns a presigned URL for downloading the original or processed image',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Download URL generated successfully',
|
||
|
schema: {
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
download_url: {
|
||
|
type: 'string',
|
||
|
example: 'https://storage.example.com/images/processed/image.jpg?expires=...',
|
||
|
},
|
||
|
expires_at: {
|
||
|
type: 'string',
|
||
|
example: '2024-01-01T13:00:00.000Z',
|
||
|
},
|
||
|
filename: {
|
||
|
type: 'string',
|
||
|
example: 'modern-kitchen-renovation.jpg',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Image not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to download this image',
|
||
|
})
|
||
|
async getImageDownloadUrl(
|
||
|
@Param('imageId') imageId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<{
|
||
|
download_url: string;
|
||
|
expires_at: string;
|
||
|
filename: string;
|
||
|
}> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const downloadInfo = await this.imagesService.getImageDownloadUrl(imageId, userId);
|
||
|
return downloadInfo;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to generate download URL');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Put(':imageId/approve')
|
||
|
@ApiOperation({
|
||
|
summary: 'Approve proposed filename',
|
||
|
description: 'Approves the AI-generated proposed filename as the final filename',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Filename approved successfully',
|
||
|
type: UpdateFilenameResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Image not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.BAD_REQUEST,
|
||
|
description: 'No proposed filename to approve',
|
||
|
})
|
||
|
async approveFilename(
|
||
|
@Param('imageId') imageId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<UpdateFilenameResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const result = await this.imagesService.approveProposedFilename(imageId, userId);
|
||
|
return result;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to approve filename');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Put(':imageId/revert')
|
||
|
@ApiOperation({
|
||
|
summary: 'Revert to original filename',
|
||
|
description: 'Reverts the image filename back to the original uploaded filename',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Filename reverted successfully',
|
||
|
type: UpdateFilenameResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Image not found',
|
||
|
})
|
||
|
async revertFilename(
|
||
|
@Param('imageId') imageId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<UpdateFilenameResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const result = await this.imagesService.revertToOriginalFilename(imageId, userId);
|
||
|
return result;
|
||
|
} catch (error) {
|
||
|
if (
|
||
|
error instanceof BadRequestException ||
|
||
|
error instanceof ForbiddenException ||
|
||
|
error instanceof NotFoundException
|
||
|
) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to revert filename');
|
||
|
}
|
||
|
}
|
||
|
}
|