审查视图

app/api/zuotang.ts 4.2 KB
202304001 authored
1
import { getServerSideConfig } from "@/app/config/server";
2 3 4 5 6 7
import type {
  CreateTaskResponse,
  GetTaskResponse,
  GetGenerateTaskResponse,
  LocalData,
} from "../types/zuotang";
202304001 authored
8 9 10 11 12
import { NextRequest, NextResponse } from "next/server";
import md5 from "spark-md5";

// 类型守卫函数
function isString(value: unknown): value is string {
13
  return typeof value === "string";
202304001 authored
14 15 16 17
}

// 统一错误响应生成器
function createErrorResponse(
18 19 20
  message: string,
  status: number,
  maxDailyUses?: number,
202304001 authored
21
): NextResponse<CreateTaskResponse> {
22 23 24 25 26 27 28 29 30 31
  const response: CreateTaskResponse = {
    status,
    message,
    data: { task_id: "" },
    maxDailyUses,
  };
  return NextResponse.json(response, {
    status,
    headers: { "Content-Type": "application/json" },
  });
202304001 authored
32 33 34 35
}

// 处理每日使用限制逻辑
function parseDailyUsage(allowNum: string, configMax: number): number {
36 37 38
  if (allowNum === "first") return configMax;
  const parsed = parseInt(allowNum, 10);
  return Number.isNaN(parsed) ? configMax : parsed;
202304001 authored
39 40 41
}

export async function handle(
42 43
  req: NextRequest,
  { params }: { params: { path: string[] } },
202304001 authored
44
) {
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  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);
202304001 authored
65 66 67
      if (!localDataObj.maxDailyUses) {
        return createErrorResponse("缺少请求参数", 400);
      }
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
      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);
202304001 authored
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
        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" },
      });
202304001 authored
131
    }
132 133 134 135 136 137 138 139 140

    return createErrorResponse("方法不允许", 405);
  } catch (error) {
    console.error("请求处理错误:", error);
    return createErrorResponse(
      error instanceof Error ? error.message : "服务器内部错误",
      500,
    );
  }
202304001 authored
141
}