zuotang.ts 4.2 KB
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,
    );
  }
}