114 lines
3.9 KiB
TypeScript
114 lines
3.9 KiB
TypeScript
import express from "express";
|
||
import { success, error } from "@/lib/responseFormat";
|
||
import { validateFields } from "@/middleware/middleware";
|
||
import u from "@/utils";
|
||
import { z } from "zod";
|
||
import { transform } from "sucrase";
|
||
const router = express.Router();
|
||
|
||
const vendorConfigSchema = z.object({
|
||
id: z.string(),
|
||
author: z.string(),
|
||
description: z.string().optional(),
|
||
name: z.string(),
|
||
icon: z.string().optional(),
|
||
inputs: z.array(
|
||
z.object({
|
||
key: z.string(),
|
||
label: z.string(),
|
||
type: z.enum(["text", "password", "url"]),
|
||
required: z.boolean(),
|
||
placeholder: z.string().optional(),
|
||
}),
|
||
),
|
||
inputValues: z.record(z.string(), z.string()),
|
||
models: z.array(
|
||
z.discriminatedUnion("type", [
|
||
z.object({
|
||
name: z.string(),
|
||
modelName: z.string(),
|
||
type: z.literal("text"),
|
||
think: z.boolean(),
|
||
}),
|
||
z.object({
|
||
name: z.string(),
|
||
modelName: z.string(),
|
||
type: z.literal("image"),
|
||
mode: z.array(z.enum(["text", "singleImage", "multiReference"])),
|
||
}),
|
||
z.object({
|
||
name: z.string(),
|
||
modelName: z.string(),
|
||
type: z.literal("video"),
|
||
mode: z.array(
|
||
z.union([
|
||
z.enum(["singleImage", "startEndRequired", "endFrameOptional", "startFrameOptional", "text", "audioReference", "videoReference"]),
|
||
z.array(z.string().regex(/^(videoReference|imageReference|audioReference):\d+$/)),
|
||
]),
|
||
),
|
||
audio: z.union([z.literal("optional"), z.boolean()]),
|
||
durationResolutionMap: z.array(
|
||
z.object({
|
||
duration: z.array(z.number()),
|
||
resolution: z.array(z.string()),
|
||
}),
|
||
),
|
||
}),
|
||
]),
|
||
),
|
||
});
|
||
|
||
export default router.post(
|
||
"/",
|
||
validateFields({
|
||
tsCode: z.string(),
|
||
}),
|
||
async (req, res) => {
|
||
const { tsCode } = req.body;
|
||
const jsCode = transform(tsCode, { transforms: ["typescript"] }).code;
|
||
const exports = u.vm(jsCode);
|
||
if (!exports) return res.status(400).send(success("脚本文件必须导出对象"));
|
||
if (!exports.textRequest) return res.status(400).send(success("脚本文件必须导出文本请求对象"));
|
||
if (!exports.imageRequest) return res.status(400).send(success("脚本文件必须导出图像请求对象"));
|
||
if (!exports.videoRequest) return res.status(400).send(success("脚本文件必须导出视频请求对象"));
|
||
if (!exports.vendor) return res.status(400).send(success("脚本文件必须导出vendor对象"));
|
||
const vendor = exports.vendor;
|
||
const result = vendorConfigSchema.safeParse(vendor);
|
||
if (!result.success) {
|
||
const issueLines = result.error.issues.map((issue, index) => {
|
||
const path = issue.path.length ? issue.path.join(".") : "root";
|
||
let detail = issue.message;
|
||
|
||
if (issue.code === "invalid_union") {
|
||
const unionDetails = [
|
||
...new Set(
|
||
issue.errors
|
||
.flat()
|
||
.map((e) => e.message)
|
||
.filter(Boolean),
|
||
),
|
||
];
|
||
if (unionDetails.length > 0) {
|
||
detail = `${issue.message}(${unionDetails.join(";")})`;
|
||
}
|
||
}
|
||
return `${index + 1}. ${path}: ${detail}`;
|
||
});
|
||
|
||
return res.status(400).send(error(`vendor配置校验失败,共 ${issueLines.length} 处:\n${issueLines.join("\n")}`));
|
||
}
|
||
|
||
if ((vendor.id as string).includes(":")) return res.status(400).send(error("id不能包含英文冒号"));
|
||
const data = await u.db("o_vendorConfig").where("id", vendor.id).first();
|
||
if (data) return res.status(500).send(error("供应商id已存在"));
|
||
const [id] = await u.db("o_vendorConfig").insert({
|
||
id: vendor.id,
|
||
inputValues: JSON.stringify(vendor.inputValues ?? {}),
|
||
models: JSON.stringify([]),
|
||
enable: vendor.id == "toonflow" ? 1 : 0,
|
||
});
|
||
u.vendor.writeCode(vendor.id, tsCode);
|
||
res.status(200).send(success(result.data));
|
||
},
|
||
);
|