|
|
import { getServerSideConfig } from "@/app/config/server";
|
|
|
import type { CreateTaskResponse, GetTaskResponse, GetGenerateTaskResponse, LocalData } from "../types/zuotang";
|
|
|
import type {
|
|
|
CreateTaskResponse,
|
|
|
GetTaskResponse,
|
|
|
GetGenerateTaskResponse,
|
|
|
LocalData,
|
|
|
} from "../types/zuotang";
|
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
import { error } from "console";
|
|
|
import { message } from "antd";
|
|
|
import md5 from "spark-md5";
|
|
|
|
|
|
|
|
|
// 类型守卫函数
|
|
|
function isString(value: unknown): value is string {
|
|
|
return typeof value === "string";
|
|
|
return typeof value === "string";
|
|
|
}
|
|
|
|
|
|
// 统一错误响应生成器
|
|
|
function createErrorResponse(
|
|
|
message: string,
|
|
|
status: number,
|
|
|
maxDailyUses?: number
|
|
|
message: string,
|
|
|
status: number,
|
|
|
maxDailyUses?: number,
|
|
|
): NextResponse<CreateTaskResponse> {
|
|
|
const response: CreateTaskResponse = {
|
|
|
status,
|
|
|
message,
|
|
|
data: { task_id: "" },
|
|
|
maxDailyUses,
|
|
|
};
|
|
|
return NextResponse.json(response, { status, headers: { "Content-Type": "application/json" } });
|
|
|
const response: CreateTaskResponse = {
|
|
|
status,
|
|
|
message,
|
|
|
data: { task_id: "" },
|
|
|
maxDailyUses,
|
|
|
};
|
|
|
return NextResponse.json(response, {
|
|
|
status,
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 处理每日使用限制逻辑
|
|
|
function parseDailyUsage(allowNum: string, configMax: number): number {
|
|
|
if (allowNum === "first") return configMax;
|
|
|
const parsed = parseInt(allowNum, 10);
|
|
|
return Number.isNaN(parsed) ? configMax : parsed;
|
|
|
if (allowNum === "first") return configMax;
|
|
|
const parsed = parseInt(allowNum, 10);
|
|
|
return Number.isNaN(parsed) ? configMax : parsed;
|
|
|
}
|
|
|
|
|
|
|
|
|
export async function handle(
|
|
|
req: NextRequest,
|
|
|
{ params }: { params: { path: string[] } }
|
|
|
req: NextRequest,
|
|
|
{ params }: { params: { path: string[] } },
|
|
|
) {
|
|
|
const config = getServerSideConfig();
|
|
|
const baseUrl = config.bgRemovalUrl;
|
|
|
const subPath = params.path.join("/");
|
|
|
const reqUrl = `${baseUrl}/api/tasks/${subPath}`;
|
|
|
|
|
|
try {
|
|
|
if (req.method === "POST") {
|
|
|
const formData = await req.formData();
|
|
|
|
|
|
// 验证访问码
|
|
|
const accessCode = formData.get("accessCode");
|
|
|
if (!isString(accessCode) || !config.codes.has(md5.hash(accessCode))) {
|
|
|
return createErrorResponse("无效访问密码!", 401);
|
|
|
}
|
|
|
|
|
|
// 解析使用限制数据
|
|
|
const localData = formData.get("localData");
|
|
|
if (!isString(localData)) {
|
|
|
return createErrorResponse("无效请求参数", 400);
|
|
|
}
|
|
|
|
|
|
const localDataObj: LocalData = JSON.parse(localData);
|
|
|
const maxDailyUses = parseDailyUsage(localDataObj.maxDailyUses, config.maxDailyUses);
|
|
|
|
|
|
if (maxDailyUses <= 0) {
|
|
|
return createErrorResponse("今日次数已用完!", 429, 0);
|
|
|
}
|
|
|
|
|
|
// 准备API请求
|
|
|
const imageFile = formData.get("image_file");
|
|
|
if (!(imageFile instanceof Blob)) {
|
|
|
return createErrorResponse("无效图片文件", 400);
|
|
|
}
|
|
|
|
|
|
const headers = new Headers({ "X-API-KEY": config.bgRemovalApiKey });
|
|
|
const newFormData = new FormData();
|
|
|
newFormData.append("image_file", imageFile);
|
|
|
|
|
|
if (subPath === "visual/r-background") {
|
|
|
newFormData.append("batch_size", "1");
|
|
|
const prompt = formData.get("prompt") as string;
|
|
|
const trimmedPrompt = prompt ? prompt.trim() : null;
|
|
|
if (!trimmedPrompt) {
|
|
|
return createErrorResponse("背景提示词不能为空!", 400);
|
|
|
}
|
|
|
newFormData.append("prompt", trimmedPrompt);
|
|
|
}
|
|
|
|
|
|
const response = await fetch(reqUrl, {
|
|
|
headers,
|
|
|
method: "POST",
|
|
|
body: newFormData,
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`API请求失败: ${response.statusText}`);
|
|
|
}
|
|
|
|
|
|
const responseData: CreateTaskResponse = await response.json();
|
|
|
responseData.maxDailyUses = maxDailyUses - 1;
|
|
|
|
|
|
return NextResponse.json(responseData, {
|
|
|
status: response.status,
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
});
|
|
|
|
|
|
} else if (req.method === "GET") {
|
|
|
const headers = { "X-API-KEY": config.bgRemovalApiKey };
|
|
|
const response = await fetch(reqUrl, { headers });
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`API请求失败: ${response.statusText}`);
|
|
|
}
|
|
|
|
|
|
const isVisualRoute = subPath.includes("visual/r-background");
|
|
|
const responseData = isVisualRoute
|
|
|
? (await response.json() as GetTaskResponse)
|
|
|
: (await response.json() as GetGenerateTaskResponse);
|
|
|
|
|
|
return NextResponse.json(responseData, {
|
|
|
status: response.status,
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
});
|
|
|
const config = getServerSideConfig();
|
|
|
const baseUrl = config.bgRemovalUrl;
|
|
|
const subPath = params.path.join("/");
|
|
|
const reqUrl = `${baseUrl}/api/tasks/${subPath}`;
|
|
|
|
|
|
try {
|
|
|
if (req.method === "POST") {
|
|
|
const formData = await req.formData();
|
|
|
|
|
|
// 验证访问码
|
|
|
const accessCode = formData.get("accessCode");
|
|
|
if (!isString(accessCode) || !config.codes.has(md5.hash(accessCode))) {
|
|
|
return createErrorResponse("无效访问密码!", 401);
|
|
|
}
|
|
|
// 解析使用限制数据
|
|
|
const localData = formData.get("localData");
|
|
|
if (!isString(localData)) {
|
|
|
return createErrorResponse("无效请求参数", 400);
|
|
|
}
|
|
|
const localDataObj: LocalData = JSON.parse(localData);
|
|
|
const maxDailyUses = parseDailyUsage(
|
|
|
localDataObj.maxDailyUses,
|
|
|
config.maxDailyUses,
|
|
|
);
|
|
|
|
|
|
if (maxDailyUses <= 0) {
|
|
|
return createErrorResponse("今日次数已用完!", 429, 0);
|
|
|
}
|
|
|
|
|
|
// 准备API请求
|
|
|
const imageFile = formData.get("image_file");
|
|
|
if (!(imageFile instanceof Blob)) {
|
|
|
return createErrorResponse("无效图片文件", 400);
|
|
|
}
|
|
|
|
|
|
const headers = new Headers({ "X-API-KEY": config.bgRemovalApiKey });
|
|
|
const newFormData = new FormData();
|
|
|
newFormData.append("image_file", imageFile);
|
|
|
|
|
|
if (subPath === "visual/r-background") {
|
|
|
newFormData.append("batch_size", "1");
|
|
|
const prompt = formData.get("prompt") as string;
|
|
|
const trimmedPrompt = prompt ? prompt.trim() : null;
|
|
|
if (!trimmedPrompt) {
|
|
|
return createErrorResponse("背景提示词不能为空!", 400);
|
|
|
}
|
|
|
|
|
|
return createErrorResponse("方法不允许", 405);
|
|
|
|
|
|
} catch (error) {
|
|
|
console.error("请求处理错误:", error);
|
|
|
return createErrorResponse(
|
|
|
error instanceof Error ? error.message : "服务器内部错误",
|
|
|
500
|
|
|
);
|
|
|
newFormData.append("prompt", trimmedPrompt);
|
|
|
}
|
|
|
|
|
|
const response = await fetch(reqUrl, {
|
|
|
headers,
|
|
|
method: "POST",
|
|
|
body: newFormData,
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`API请求失败: ${response.statusText}`);
|
|
|
}
|
|
|
|
|
|
const responseData: CreateTaskResponse = await response.json();
|
|
|
responseData.maxDailyUses = maxDailyUses - 1;
|
|
|
|
|
|
return NextResponse.json(responseData, {
|
|
|
status: response.status,
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
});
|
|
|
} else if (req.method === "GET") {
|
|
|
const headers = { "X-API-KEY": config.bgRemovalApiKey };
|
|
|
const response = await fetch(reqUrl, { headers });
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`API请求失败: ${response.statusText}`);
|
|
|
}
|
|
|
|
|
|
const isVisualRoute = subPath.includes("visual/r-background");
|
|
|
const responseData = isVisualRoute
|
|
|
? ((await response.json()) as GetTaskResponse)
|
|
|
: ((await response.json()) as GetGenerateTaskResponse);
|
|
|
|
|
|
return NextResponse.json(responseData, {
|
|
|
status: response.status,
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return createErrorResponse("方法不允许", 405);
|
|
|
} catch (error) {
|
|
|
console.error("请求处理错误:", error);
|
|
|
return createErrorResponse(
|
|
|
error instanceof Error ? error.message : "服务器内部错误",
|
|
|
500,
|
|
|
);
|
|
|
}
|
|
|
} |
...
|
...
|
|