import { Controller, Get, Delete, Query, HttpCode, HttpStatus, Res, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger'; import type { Response } from 'express'; import { IdriveE2StorageService } from './idrive-e2-storage.service'; import { FileType } from './enums/file-type.enum'; import { LogToDiscord } from 'src/shared/decorators/log-to-discord.decorator'; @ApiTags('Upload: IDrive e2 Storage') @Controller('idrive-e2') @LogToDiscord() export class IdriveE2StorageController { constructor( private readonly idriveE2StorageService: IdriveE2StorageService, ) {} @Get('presigned-upload') @ApiOperation({ summary: 'Get presigned upload URL' }) @ApiQuery({ name: 'fileName', required: true }) @ApiQuery({ name: 'fileType', enum: FileType, required: false }) async getPresignedUploadUrl( @Query('fileName') fileName: string, @Query('fileType') fileType?: FileType, ) { return this.idriveE2StorageService.getPresignedUploadUrl( fileName, fileType, ); } @Get('presigned-url') @ApiOperation({ summary: 'Get presigned download URL (called by Cloudflare Worker)', }) @ApiQuery({ name: 'filePath', required: true }) async getPresignedUrl(@Query('filePath') filePath: string) { if (!filePath) { throw new Error('filePath is required'); } const result = await this.idriveE2StorageService.getPresignedDownloadUrl( filePath, 3600, // 1 hour ); return { presignedUrl: result.downloadUrl, filePath: result.filePath, expiresIn: result.expiresIn, }; } @Get('resize') @ApiOperation({ summary: 'Resize image (called by Cloudflare Worker)' }) @ApiQuery({ name: 'path', required: true }) @ApiQuery({ name: 'w', required: false }) @ApiQuery({ name: 'h', required: false }) @ApiQuery({ name: 'q', required: false }) async resizeImage( @Res() res: Response, @Query('path') path: string, @Query('w') width?: string, @Query('h') height?: string, @Query('q') quality?: string, ) { const w = width ? parseInt(width, 10) : undefined; const h = height ? parseInt(height, 10) : undefined; const q = quality ? parseInt(quality, 10) : undefined; const { buffer, contentType } = await this.idriveE2StorageService.resizeImage(path, w, h, q); res.set({ 'Content-Type': contentType, 'Cache-Control': 'public, max-age=31536000', }); res.send(buffer); } @Delete('by-url') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Delete file by URL' }) async deleteFileByUrl(@Query('url') url: string) { const result = await this.idriveE2StorageService.deleteFileByUrl(url); return { message: 'File deleted successfully', ...result, }; } }