zuotang.ts
4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { getServerSideConfig } from "@/app/config/server";
import type {
CreateTaskResponse,
GetTaskResponse,
GetGenerateTaskResponse,
LocalData,
} from "../types/zuotang";
import { NextRequest, NextResponse } from "next/server";
import md5 from "spark-md5";
// 类型守卫函数
function isString(value: unknown): value is string {
return typeof value === "string";
}
// 统一错误响应生成器
function createErrorResponse(
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" },
});
}
// 处理每日使用限制逻辑
function parseDailyUsage(allowNum: string, configMax: number): number {
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[] } },
) {
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);
if (!localDataObj.maxDailyUses) {
return createErrorResponse("缺少请求参数", 400);
}
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" },
});
}
return createErrorResponse("方法不允许", 405);
} catch (error) {
console.error("请求处理错误:", error);
return createErrorResponse(
error instanceof Error ? error.message : "服务器内部错误",
500,
);
}
}