import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { fetchImageBufferFromUrl } from "@/lib/ai/rmbg/service"; import { uploadFile } from "@/lib/storage/service"; export const runtime = "nodejs"; export const maxDuration = 120; const DEFAULT_WIDTH = 790; const DEFAULT_HEIGHT = 1300; const MAX_DIMENSION = 4096; function bufferToArrayBuffer(buffer: Buffer): ArrayBuffer { return buffer.buffer.slice( buffer.byteOffset, buffer.byteOffset + buffer.byteLength, ) as ArrayBuffer; } function normalizeDimension(value: unknown, fallback: number) { const next = Math.round(Number(value)); if (!Number.isFinite(next) || next <= 0) return fallback; return Math.min(MAX_DIMENSION, Math.max(64, next)); } function normalizeFit(value: unknown) { const fit = String(value || "").trim(); return fit === "contain" ? "contain" : "cover-top"; } async function createThumbnailBuffer(buffer: Buffer) { const sharp = (await import("sharp")).default; return sharp(buffer, { failOn: "none" }) .resize(500, null, { fit: "inside", withoutEnlargement: true, }) .jpeg({ quality: 82, mozjpeg: true }) .toBuffer(); } export async function POST(request: NextRequest) { try { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const body = (await request.json().catch(() => ({}))) as Record; const imageUrl = String(body.imageUrl || "").trim(); if (!/^https?:\/\//i.test(imageUrl)) { return NextResponse.json({ success: false, error: "缺少有效图片地址" }, { status: 400 }); } const width = normalizeDimension(body.width, DEFAULT_WIDTH); const height = normalizeDimension(body.height, DEFAULT_HEIGHT); const fit = normalizeFit(body.fit); const sourceBuffer = await fetchImageBufferFromUrl(imageUrl); const sharp = (await import("sharp")).default; const pipeline = sharp(sourceBuffer, { failOn: "none" }).rotate(); const normalizedBuffer = fit === "contain" ? await pipeline .resize(width, height, { fit: "contain", position: "centre", background: { r: 255, g: 255, b: 255, alpha: 1 }, withoutEnlargement: false, }) .png() .toBuffer() : await pipeline .resize(width, height, { fit: "cover", position: "top", withoutEnlargement: false, }) .png() .toBuffer(); const thumbnailBuffer = await createThumbnailBuffer(normalizedBuffer); const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const [originalUpload, thumbnailUpload] = await Promise.all([ uploadFile(`agent-detail-normalized-${suffix}.png`, bufferToArrayBuffer(normalizedBuffer)), uploadFile(`agent-detail-normalized-thumb-${suffix}.jpg`, bufferToArrayBuffer(thumbnailBuffer)), ]); return NextResponse.json({ success: true, data: { originalUrl: originalUpload.url, thumbnailUrl: thumbnailUpload.url, imageSize: `${width}x${height}`, }, }); } catch (error) { console.error("[Agent Detail Image Normalize] Failed:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "详情图尺寸归一化失败", }, { status: 500 }, ); } }