first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:09:32 +03:00
commit 71eff2d979
78 changed files with 10173 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
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;
}