Files
Beyhan Oğur 71eff2d979 first commit
2026-04-26 22:09:32 +03:00

183 lines
5.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from "next/server";
import { authenticateAPIRequest } from "@/app/lib/api-auth";
import { writeFile, mkdir } from "fs/promises";
import { join } from "path";
import sharp from "sharp";
import { db } from "@/db";
import { images } from "@/db/schema";
import { nanoid } from "nanoid";
/**
* POST /api/v1/images/upload
* Resim yükle ve manipüle et
*
* Headers:
* - Authorization: Bearer <jwt_token>
* - Content-Type: multipart/form-data
*
* Body (FormData):
* - file: Resim dosyası
* - width: Genişlik (px) - opsiyonel, default: 800
* - height: Yükseklik (px) - opsiyonel, default: 600
* - quality: Kalite (1-100) - opsiyonel, default: 90
* - format: Format (jpeg, png, webp, avif) - opsiyonel, default: jpeg
*/
export async function POST(request: NextRequest) {
const auth = await authenticateAPIRequest(request);
if (!auth.authenticated) {
return NextResponse.json({ error: auth.error }, { status: 401 });
}
try {
const formData = await request.formData();
const file = formData.get("file") as File;
if (!file) {
return NextResponse.json(
{ error: "Dosya bulunamadı" },
{ status: 400 }
);
}
// Dosya boyutu kontrolü (max 10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024;
if (file.size > MAX_FILE_SIZE) {
return NextResponse.json(
{ error: "Dosya boyutu çok büyük. Maksimum 10MB olmalıdır." },
{ status: 400 }
);
}
// Dosya tipi kontrolü
const allowedMimeTypes = ["image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/avif"];
if (!allowedMimeTypes.includes(file.type)) {
return NextResponse.json(
{ error: "Geçersiz dosya tipi. Sadece resim dosyaları kabul edilir." },
{ status: 400 }
);
}
// Parametreleri al
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
let processedBuffer = sharp(buffer).resize(width, height, {
fit: "cover",
position: "center",
withoutEnlargement: false,
});
// 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 kaydet
const fileId = nanoid();
const originalName = file.name;
const fileExtension = normalizedFormat === "jpeg" ? "jpg" : normalizedFormat;
const fileName = `${fileId}.${fileExtension}`;
const uploadsDir = join(process.cwd(), "public", "uploads");
try {
await mkdir(uploadsDir, { recursive: true });
} catch (mkdirError: any) {
console.error("Uploads klasörü oluşturulamadı:", mkdirError);
throw new Error(`Uploads klasörü oluşturulamadı: ${mkdirError.message}`);
}
const filePath = join(uploadsDir, fileName);
try {
await writeFile(filePath, processedImage);
} catch (writeError: any) {
console.error("Dosya yazılamadı:", writeError);
throw new Error(`Dosya yazılamadı: ${writeError.message}`);
}
// Veritabanına kaydet
const imageUrl = `/uploads/${fileName}`;
const imageId = nanoid();
await db.insert(images).values({
id: imageId,
userId: auth.userId!,
originalName,
fileName,
filePath: filePath,
url: imageUrl,
width: metadata.width || null,
height: metadata.height || null,
quality,
format: normalizedFormat,
fileSize: processedImage.length,
});
// Base URL
const baseUrl = getBaseUrl(request);
const fullImageUrl = `${baseUrl}${imageUrl}`;
return NextResponse.json({
success: true,
message: "Resim başarıyla yüklendi",
data: {
image: {
id: imageId,
url: fullImageUrl,
width: metadata.width,
height: metadata.height,
format: normalizedFormat,
fileSize: processedImage.length,
},
},
});
} catch (error: any) {
console.error("API - Upload hatası:", error);
return NextResponse.json(
{ error: error.message || "Yükleme başarısız" },
{ status: 500 }
);
}
}
function getBaseUrl(request: NextRequest): string {
if (process.env.NEXT_PUBLIC_APP_URL) {
return process.env.NEXT_PUBLIC_APP_URL;
}
if (process.env.APP_URL) {
return process.env.APP_URL;
}
const forwardedHost = request.headers.get("x-forwarded-host");
const forwardedProto = request.headers.get("x-forwarded-proto");
if (forwardedHost && forwardedProto) {
return `${forwardedProto}://${forwardedHost}`;
}
return request.nextUrl.origin;
}