2026-06-02 20:42:59 +08:00

106 lines
3.5 KiB
TypeScript

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<string, unknown>;
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 },
);
}
}