import { NextRequest, NextResponse } from "next/server"; import sharp from "sharp"; import { db } from "@/db"; import { images } from "@/db/schema"; import { nanoid } from "nanoid"; import { auth } from "@/app/lib/auth"; import { uploadToR2, getContentType } from "@/app/lib/r2-storage"; async function getUserId(request: NextRequest): Promise { try { const session = await auth.api.getSession({ headers: request.headers, }); return session?.user?.id || null; } catch { return null; } } export async function POST(request: NextRequest) { try { const userId = await getUserId(request); if (!userId) { return NextResponse.json( { message: "Yetkisiz erişim" }, { status: 401 } ); } const formData = await request.formData(); const file = formData.get("file") as File; if (!file) { return NextResponse.json( { message: "Dosya bulunamadı" }, { status: 400 } ); } // File size validation (max 10MB) const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB if (file.size > MAX_FILE_SIZE) { return NextResponse.json( { message: "Dosya boyutu çok büyük. Maksimum 10MB olmalıdır." }, { status: 400 } ); } // File type validation const allowedMimeTypes = ["image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/avif"]; if (!allowedMimeTypes.includes(file.type)) { return NextResponse.json( { message: "Geçersiz dosya tipi. Sadece resim dosyaları kabul edilir." }, { status: 400 } ); } // Input validation const widthInput = formData.get("width") as string; const heightInput = formData.get("height") as string; const qualityInput = formData.get("quality") as string; const formatInput = (formData.get("format") as string) || "jpeg"; const width = Math.max(1, Math.min(10000, parseInt(widthInput) || 800)); const height = Math.max(1, Math.min(10000, parseInt(heightInput) || 600)); const quality = Math.max(1, Math.min(100, parseInt(qualityInput) || 90)); const allowedFormats = ["jpeg", "jpg", "png", "webp", "avif"]; const format = allowedFormats.includes(formatInput) ? formatInput : "jpeg"; const bytes = await file.arrayBuffer(); const buffer = Buffer.from(bytes); // Resim manipülasyonu - Tam istenen boyuta getir (crop ile, bozmadan) // fit: "cover" kullanarak resmi tam boyuta getiriyoruz // Aspect ratio korunur, fazla kısımlar ortadan kesilir (crop) let processedBuffer = sharp(buffer).resize(width, height, { fit: "cover", // Tam boyuta getir, aspect ratio koru, fazla kısımları kes position: "center", // Ortadan crop yap withoutEnlargement: false, // Gerekirse büyüt de tam boyuta getir }); // Format ve kalite ayarları const normalizedFormat = format === "jpg" ? "jpeg" : format; if (normalizedFormat === "jpeg") { processedBuffer = processedBuffer.jpeg({ quality }); } else if (normalizedFormat === "png") { processedBuffer = processedBuffer.png({ quality }); } else if (normalizedFormat === "webp") { processedBuffer = processedBuffer.webp({ quality }); } else if (normalizedFormat === "avif") { processedBuffer = processedBuffer.avif({ quality }); } const processedImage = await processedBuffer.toBuffer(); const metadata = await sharp(processedImage).metadata(); // Dosya adı oluştur const fileId = nanoid(); const originalName = file.name; const fileExtension = normalizedFormat === "jpeg" ? "jpg" : normalizedFormat; const fileName = `${fileId}.${fileExtension}`; // R2'ye yükle const contentType = getContentType(fileExtension); const r2Url = await uploadToR2({ buffer: processedImage, fileName, contentType, }); // Veritabanına kaydet const imageId = nanoid(); await db.insert(images).values({ id: imageId, userId, originalName, fileName, filePath: fileName, // R2'de sadece fileName yeterli url: r2Url, // R2'nin tam URL'si width: metadata.width || null, height: metadata.height || null, quality, format: normalizedFormat, fileSize: processedImage.length, }); const fullImageUrl = r2Url; return NextResponse.json({ message: "Resim başarıyla yüklendi", image: { id: imageId, url: fullImageUrl, width: metadata.width, height: metadata.height, }, }); } catch (error: any) { console.error("Upload hatası:", error); console.error("Error stack:", error?.stack); console.error("Error message:", error?.message); // Production'da detaylı hata mesajı döndür (debug için) const errorMessage = process.env.NODE_ENV === "production" ? `Yükleme başarısız: ${error?.message || "Bilinmeyen hata"}` : "Yükleme başarısız"; return NextResponse.json( { message: errorMessage }, { status: 500 } ); } }