275 lines
7.6 KiB
TypeScript
275 lines
7.6 KiB
TypeScript
![]() |
import {
|
||
|
Controller,
|
||
|
Post,
|
||
|
Get,
|
||
|
Param,
|
||
|
Body,
|
||
|
UploadedFiles,
|
||
|
UseInterceptors,
|
||
|
UseGuards,
|
||
|
Request,
|
||
|
HttpStatus,
|
||
|
BadRequestException,
|
||
|
PayloadTooLargeException,
|
||
|
ForbiddenException,
|
||
|
} from '@nestjs/common';
|
||
|
import { FilesInterceptor } from '@nestjs/platform-express';
|
||
|
import { ApiTags, ApiOperation, ApiResponse, ApiConsumes, ApiBearerAuth } from '@nestjs/swagger';
|
||
|
import { JwtAuthGuard } from '../auth/auth.guard';
|
||
|
import { BatchesService } from './batches.service';
|
||
|
import { CreateBatchDto, BatchUploadResponseDto } from './dto/create-batch.dto';
|
||
|
import { BatchStatusResponseDto, BatchListResponseDto } from './dto/batch-status.dto';
|
||
|
|
||
|
@ApiTags('batches')
|
||
|
@Controller('api/batch')
|
||
|
@UseGuards(JwtAuthGuard)
|
||
|
@ApiBearerAuth()
|
||
|
export class BatchesController {
|
||
|
constructor(private readonly batchesService: BatchesService) {}
|
||
|
|
||
|
@Post()
|
||
|
@UseInterceptors(FilesInterceptor('files', 1000)) // Max 1000 files per batch
|
||
|
@ApiOperation({
|
||
|
summary: 'Upload batch of images for processing',
|
||
|
description: 'Uploads multiple images and starts batch processing with AI analysis and SEO filename generation'
|
||
|
})
|
||
|
@ApiConsumes('multipart/form-data')
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batch created successfully',
|
||
|
type: BatchUploadResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.BAD_REQUEST,
|
||
|
description: 'Invalid files or missing data',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.PAYLOAD_TOO_LARGE,
|
||
|
description: 'File size or count exceeds limits',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Insufficient quota remaining',
|
||
|
})
|
||
|
async uploadBatch(
|
||
|
@UploadedFiles() files: Express.Multer.File[],
|
||
|
@Body() createBatchDto: CreateBatchDto,
|
||
|
@Request() req: any,
|
||
|
): Promise<BatchUploadResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
// Validate files are provided
|
||
|
if (!files || files.length === 0) {
|
||
|
throw new BadRequestException('No files provided');
|
||
|
}
|
||
|
|
||
|
// Check file count limits
|
||
|
if (files.length > 1000) {
|
||
|
throw new PayloadTooLargeException('Maximum 1000 files per batch');
|
||
|
}
|
||
|
|
||
|
// Process the batch upload
|
||
|
const result = await this.batchesService.createBatch(userId, files, createBatchDto);
|
||
|
|
||
|
return result;
|
||
|
|
||
|
} catch (error) {
|
||
|
if (error instanceof BadRequestException ||
|
||
|
error instanceof PayloadTooLargeException ||
|
||
|
error instanceof ForbiddenException) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to process batch upload');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get(':batchId/status')
|
||
|
@ApiOperation({
|
||
|
summary: 'Get batch processing status',
|
||
|
description: 'Returns current status and progress of batch processing'
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batch status retrieved successfully',
|
||
|
type: BatchStatusResponseDto,
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Batch not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to access this batch',
|
||
|
})
|
||
|
async getBatchStatus(
|
||
|
@Param('batchId') batchId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<BatchStatusResponseDto> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const status = await this.batchesService.getBatchStatus(batchId, userId);
|
||
|
return status;
|
||
|
|
||
|
} catch (error) {
|
||
|
if (error instanceof BadRequestException || error instanceof ForbiddenException) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to get batch status');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get()
|
||
|
@ApiOperation({
|
||
|
summary: 'List user batches',
|
||
|
description: 'Returns list of all batches for the authenticated user'
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batches retrieved successfully',
|
||
|
type: [BatchListResponseDto],
|
||
|
})
|
||
|
async getUserBatches(
|
||
|
@Request() req: any,
|
||
|
): Promise<BatchListResponseDto[]> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const batches = await this.batchesService.getUserBatches(userId);
|
||
|
return batches;
|
||
|
|
||
|
} catch (error) {
|
||
|
throw new BadRequestException('Failed to get user batches');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Post(':batchId/cancel')
|
||
|
@ApiOperation({
|
||
|
summary: 'Cancel batch processing',
|
||
|
description: 'Cancels ongoing batch processing'
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batch cancelled successfully',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Batch not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.FORBIDDEN,
|
||
|
description: 'Not authorized to cancel this batch',
|
||
|
})
|
||
|
async cancelBatch(
|
||
|
@Param('batchId') batchId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<{ message: string }> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
await this.batchesService.cancelBatch(batchId, userId);
|
||
|
|
||
|
return { message: 'Batch cancelled successfully' };
|
||
|
|
||
|
} catch (error) {
|
||
|
if (error instanceof BadRequestException || error instanceof ForbiddenException) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to cancel batch');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Post(':batchId/retry')
|
||
|
@ApiOperation({
|
||
|
summary: 'Retry failed batch processing',
|
||
|
description: 'Retries processing for failed images in a batch'
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'Batch retry started successfully',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Batch not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.BAD_REQUEST,
|
||
|
description: 'Batch is not in a retryable state',
|
||
|
})
|
||
|
async retryBatch(
|
||
|
@Param('batchId') batchId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<{ message: string; retry_count: number }> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const retryCount = await this.batchesService.retryBatch(batchId, userId);
|
||
|
|
||
|
return {
|
||
|
message: 'Batch retry started successfully',
|
||
|
retry_count: retryCount
|
||
|
};
|
||
|
|
||
|
} catch (error) {
|
||
|
if (error instanceof BadRequestException || error instanceof ForbiddenException) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to retry batch');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Get(':batchId/download')
|
||
|
@ApiOperation({
|
||
|
summary: 'Download processed batch as ZIP',
|
||
|
description: 'Returns a ZIP file containing all processed images with new filenames'
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.OK,
|
||
|
description: 'ZIP file download started',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.NOT_FOUND,
|
||
|
description: 'Batch not found',
|
||
|
})
|
||
|
@ApiResponse({
|
||
|
status: HttpStatus.BAD_REQUEST,
|
||
|
description: 'Batch processing not completed',
|
||
|
})
|
||
|
async downloadBatch(
|
||
|
@Param('batchId') batchId: string,
|
||
|
@Request() req: any,
|
||
|
): Promise<{ download_url: string; expires_at: string }> {
|
||
|
try {
|
||
|
const userId = req.user?.id;
|
||
|
if (!userId) {
|
||
|
throw new BadRequestException('User not authenticated');
|
||
|
}
|
||
|
|
||
|
const downloadInfo = await this.batchesService.generateBatchDownload(batchId, userId);
|
||
|
|
||
|
return downloadInfo;
|
||
|
|
||
|
} catch (error) {
|
||
|
if (error instanceof BadRequestException || error instanceof ForbiddenException) {
|
||
|
throw error;
|
||
|
}
|
||
|
throw new BadRequestException('Failed to generate batch download');
|
||
|
}
|
||
|
}
|
||
|
}
|