import { NextRequest, NextResponse } from "next/server"; import { readFile, readdir } from "fs/promises"; import path from "path"; import type { ImageCompressConfig } from "@/types"; import { saveProcessedFile, cleanupFile, sanitizeFilename, } from "@/lib/file-storage"; import { compressImage, getImageMetadata, validateImageBuffer, validateCompressConfig, } from "@/lib/image-processor"; export const runtime = "nodejs"; const UPLOAD_DIR = process.env.TEMP_DIR || path.join(process.cwd(), ".temp", "uploads"); interface ProcessRequest { fileId: string; config: ImageCompressConfig; } /** * Find file by ID in upload directory */ async function findUploadedFile(fileId: string): Promise<{ buffer: Buffer; name: string } | null> { try { const files = await readdir(UPLOAD_DIR); const file = files.find((f) => f.startsWith(`${fileId}.`)); if (!file) { return null; } const filePath = path.join(UPLOAD_DIR, file); const buffer = await readFile(filePath); // Validate it's actually an image const isValid = await validateImageBuffer(buffer); if (!isValid) { return null; } // Extract original name from file (remove UUID prefix) const originalName = file.substring(fileId.length + 1); return { buffer, name: originalName }; } catch { return null; } } export async function POST(request: NextRequest) { try { const body: ProcessRequest = await request.json(); const { fileId, config } = body; // Validate request if (!fileId || typeof fileId !== "string") { return NextResponse.json( { success: false, error: "Valid file ID is required" }, { status: 400 } ); } // Sanitize file ID to prevent path traversal const sanitizedId = sanitizeFilename(fileId).replace(/\.[^.]+$/, ""); if (sanitizedId !== fileId) { return NextResponse.json( { success: false, error: "Invalid file ID" }, { status: 400 } ); } // Validate config const configValidation = validateCompressConfig(config); if (!configValidation.valid) { return NextResponse.json( { success: false, error: configValidation.error }, { status: 400 } ); } // Find uploaded file const uploadedFile = await findUploadedFile(fileId); if (!uploadedFile) { return NextResponse.json( { success: false, error: "File not found or expired" }, { status: 404 } ); } // Get original metadata const originalMetadata = await getImageMetadata(uploadedFile.buffer); // Process image const result = await compressImage(uploadedFile.buffer, config); // Save processed file const outputFormat = config.format === "original" ? originalMetadata.format : config.format; const downloadInfo = await saveProcessedFile( fileId, // Original file ID for tracking result.buffer, outputFormat, uploadedFile.name ); // Cleanup original file await cleanupFile(fileId); return NextResponse.json({ success: true, fileUrl: downloadInfo.fileUrl, filename: downloadInfo.filename, metadata: { format: result.format, quality: config.quality, compressionRatio: result.compressionRatio, originalSize: result.originalSize, compressedSize: result.compressedSize, width: result.width, height: result.height, }, }); } catch (error) { console.error("Processing error:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "Processing failed", }, { status: 500 } ); } }