278 lines
9.4 KiB
TypeScript
Raw 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 { auth } from "@/app/(auth)/auth";
import { db } from "@/lib/db";
import { systemSettings, generatedImage } from "@/lib/db/schema";
import { eq } from "drizzle-orm";
import { uploadFile } from "@/lib/storage/service";
// PicWish API 需要公网可访问的 HTTP/HTTPS URL
// 获取去水印API配置
async function getWatermarkRemoveConfig() {
try {
const setting = await db
.select()
.from(systemSettings)
.where(eq(systemSettings.key, "watermark_remove_config"))
.limit(1);
if (setting.length > 0 && setting[0].value) {
return setting[0].value as { apiKey: string };
}
return null;
} catch (error) {
console.error("[WatermarkRemove] 获取配置失败:", error);
return null;
}
}
// 轮询获取处理结果
async function pollForResult(taskId: string, apiKey: string): Promise<{ success: boolean; imageUrl?: string; error?: string }> {
const maxAttempts = 30; // 最多轮询30次(30秒)
const pollInterval = 1000; // 每次间隔1秒
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
const response = await fetch(
`https://techsz.aoscdn.com/api/tasks/visual/external/watermark-remove/${taskId}`,
{
method: "GET",
headers: {
"X-API-KEY": apiKey,
},
}
);
if (!response.ok) {
const errorText = await response.text();
console.error(`[WatermarkRemove] 轮询失败 (attempt ${attempt + 1}):`, errorText);
// 继续轮询
await new Promise(resolve => setTimeout(resolve, pollInterval));
continue;
}
const result = await response.json();
console.log(`[WatermarkRemove] 轮询结果 (attempt ${attempt + 1}):`, JSON.stringify(result).substring(0, 200));
// 检查任务状态
if (result.data?.state === 1) {
// 任务完成 - PicWish API 返回 "file" 字段
const imageUrl = result.data?.file || result.data?.image;
if (imageUrl) {
return { success: true, imageUrl };
}
return { success: false, error: "处理完成但未返回图片URL" };
} else if (result.data?.state === -1 || result.data?.state < 0) {
// 任务失败
return { success: false, error: result.data?.error || "去水印处理失败" };
}
// state === 0 表示处理中,继续轮询
await new Promise(resolve => setTimeout(resolve, pollInterval));
} catch (error) {
console.error(`[WatermarkRemove] 轮询错误 (attempt ${attempt + 1}):`, error);
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
}
return { success: false, error: "处理超时,请稍后重试" };
}
export async function POST(request: NextRequest) {
try {
// 验证用户登录
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json({ error: "请先登录" }, { status: 401 });
}
const startTime = Date.now();
// 获取请求参数
const body = await request.json();
const { image_url } = body;
if (!image_url) {
return NextResponse.json(
{ error: "请提供图片URL" },
{ status: 400 }
);
}
// 获取API配置
const config = await getWatermarkRemoveConfig();
if (!config?.apiKey) {
return NextResponse.json(
{ error: "去水印服务未配置,请联系管理员在系统设置中配置API密钥" },
{ status: 500 }
);
}
console.log("[WatermarkRemove] 使用原图URL:", image_url.substring(0, 100) + "...");
// 处理图片URL,确保获取公网可访问的URL
let publicImageUrl: string;
// 如果是 base64 格式的 data URL,先上传到 S3
if (image_url.startsWith("data:")) {
console.log("[WatermarkRemove] 检测到base64格式,上传到S3...");
try {
// 解析 base64 data URL
const matches = image_url.match(/^data:image\/(\w+);base64,(.+)$/);
if (!matches) {
return NextResponse.json(
{ error: "无效的图片格式" },
{ status: 400 }
);
}
const ext = matches[1] === "jpeg" ? "jpg" : matches[1];
const base64Data = matches[2];
const buffer = Buffer.from(base64Data, "base64");
// 上传到 S3
const filename = `watermark-input-${Date.now()}.${ext}`;
const uploadResult = await uploadFile(filename, buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
publicImageUrl = uploadResult.url;
console.log("[WatermarkRemove] base64图片已上传到S3:", publicImageUrl);
} catch (uploadError) {
console.error("[WatermarkRemove] base64上传失败:", uploadError);
return NextResponse.json(
{ error: "图片上传失败" },
{ status: 500 }
);
}
}
// 如果是 HTTP/HTTPS URL,下载后重新上传确保公网可访问
else if (image_url.startsWith("http://") || image_url.startsWith("https://")) {
try {
console.log("[WatermarkRemove] 下载原图并上传到S3...");
const imageResponse = await fetch(image_url);
if (!imageResponse.ok) {
throw new Error("下载原图失败");
}
const imageBuffer = await imageResponse.arrayBuffer();
const filename = `watermark-input-${Date.now()}.jpg`;
const uploadResult = await uploadFile(filename, imageBuffer);
publicImageUrl = uploadResult.url;
console.log("[WatermarkRemove] 图片已上传到S3:", publicImageUrl);
} catch (downloadError) {
console.error("[WatermarkRemove] 下载/上传原图失败:", downloadError);
// 如果下载失败,尝试使用原始URL
console.log("[WatermarkRemove] 尝试使用原始URL");
publicImageUrl = image_url;
}
} else {
return NextResponse.json(
{ error: "图片URL格式无效" },
{ status: 400 }
);
}
console.log("[WatermarkRemove] 开始处理图片:", publicImageUrl);
// 创建去水印任务
const formData = new FormData();
formData.append("sync", "0");
formData.append("url", publicImageUrl);
const createResponse = await fetch(
"https://techsz.aoscdn.com/api/tasks/visual/external/watermark-remove",
{
method: "POST",
headers: {
"X-API-KEY": config.apiKey,
},
body: formData,
}
);
if (!createResponse.ok) {
const errorText = await createResponse.text();
console.error("[WatermarkRemove] 创建任务失败:", errorText);
return NextResponse.json(
{ error: "创建去水印任务失败" },
{ status: 500 }
);
}
const createResult = await createResponse.json();
console.log("[WatermarkRemove] 创建任务结果:", JSON.stringify(createResult).substring(0, 200));
const taskId = createResult.data?.task_id;
if (!taskId) {
return NextResponse.json(
{ error: "未获取到任务ID" },
{ status: 500 }
);
}
// 轮询获取结果
const pollResult = await pollForResult(taskId, config.apiKey);
if (!pollResult.success) {
return NextResponse.json(
{ error: pollResult.error || "去水印处理失败" },
{ status: 500 }
);
}
// 下载处理后的图片并上传到S3
const processedImageUrl = pollResult.imageUrl!;
console.log("[WatermarkRemove] 处理完成,结果URL:", processedImageUrl);
try {
// 下载处理后的图片
const imageResponse = await fetch(processedImageUrl);
if (!imageResponse.ok) {
throw new Error("下载处理后的图片失败");
}
const imageBuffer = await imageResponse.arrayBuffer();
// 上传到S3
const filename = `watermark-removed-${Date.now()}.png`;
const uploadResult = await uploadFile(filename, imageBuffer);
const processingTime = (Date.now() - startTime) / 1000;
console.log(`[WatermarkRemove] 处理完成,耗时: ${processingTime}s`);
// 保存到相册/历史出图记录
try {
await db.insert(generatedImage).values({
userId: session.user.id,
thumbnailUrl: uploadResult.url,
originalUrl: uploadResult.url,
imageSize: "2K",
prompt: "去水印处理",
modelId: "watermark-remove",
});
console.log(`[WatermarkRemove] 已保存到相册: ${uploadResult.url.substring(0, 80)}...`);
} catch (saveError) {
console.error("[WatermarkRemove] 保存到相册失败:", saveError);
}
return NextResponse.json({
success: true,
original_url: uploadResult.url,
thumbnail_url: uploadResult.url,
processing_time: processingTime,
});
} catch (uploadError) {
console.error("[WatermarkRemove] 上传结果失败:", uploadError);
// 如果上传失败,直接返回原始URL
const processingTime = (Date.now() - startTime) / 1000;
return NextResponse.json({
success: true,
original_url: processedImageUrl,
thumbnail_url: processedImageUrl,
processing_time: processingTime,
});
}
} catch (error) {
console.error("[WatermarkRemove] 处理错误:", error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : "去水印处理失败" },
{ status: 500 }
);
}
}