d2o / app.js
misonL's picture
Upload 22 files
b5569e9 verified
Raw
History Blame Contribute Delete
7.35 kB
// app.js
import express from "express";
import bodyParser from "body-parser";
import fetch from "node-fetch";
import FormData from "form-data";
import { PassThrough } from "stream";
import { log } from './config/logger.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { WebSocketServer } from 'ws';
import { Tail } from 'tail';
import http from 'http';
// 引入各 bot 类型的处理器
import chatHandler from "./botType/chatHandler.js";
import completionHandler from "./botType/completionHandler.js";
import workflowHandler from "./botType/workflowHandler.js";
// 从 utils.js 中导入工具函数
import {
sanitizeLog,
logRequest,
logResponse,
logApiCall,
generateId,
} from "./botType/utils.js";
// 定义 parseConfig 函数
function parseConfig(authHeader, modelParam) {
log("debug", "开始解析配置", {
authHeader: authHeader ? authHeader.substring(0, 20) + "..." : "No Auth Header",
modelParam,
});
let config = {};
// 从 Authorization header 获取信息
if (!authHeader || !authHeader.startsWith("Bearer ")) {
log("error", "缺少或无效的 Authorization header");
throw new Error("Missing or invalid Authorization header");
}
const [_, token] = authHeader.split("Bearer ");
const tokenParts = token.split("|");
// 方式一:所有信息都在 Authorization header 中
if (tokenParts.length >= 3) {
const [difyApiUrl, apiKey, botType, inputVariable, outputVariable] = tokenParts;
config = {
DIFY_API_URL: difyApiUrl,
API_KEY: apiKey,
BOT_TYPE: botType,
INPUT_VARIABLE: inputVariable || "",
OUTPUT_VARIABLE: outputVariable || "",
};
log("info", "配置解析成功 - 方式一", config);
return config;
}
// 方式二和方式三的处理
if (tokenParts.length === 1) {
const singleValue = tokenParts[0].trim();
// 解析 model 参数
if (!modelParam) {
log("error", "缺少 model 参数");
throw new Error("Missing model parameter");
}
const modelParts = modelParam.split("|");
if (modelParts[0] !== "dify" || modelParts.length < 3) {
log("error", "无效的 model 参数格式");
throw new Error("Invalid model parameter format");
}
// 方式二:Authorization 是 API_KEY
if (singleValue.length > 0 && !singleValue.includes("http")) {
config.API_KEY = singleValue;
const [_, botType, difyApiUrl, inputVariable, outputVariable] = modelParts;
config.DIFY_API_URL = difyApiUrl;
config.BOT_TYPE = botType;
config.INPUT_VARIABLE = inputVariable || "";
config.OUTPUT_VARIABLE = outputVariable || "";
log("info", "配置解析成功 - 方式二", config);
}
// 方式三:Authorization 是 DIFY_API_URL
else {
config.DIFY_API_URL = singleValue;
const [_, apiKey, botType, inputVariable, outputVariable] = modelParts;
config.API_KEY = apiKey;
config.BOT_TYPE = botType;
config.INPUT_VARIABLE = inputVariable || "";
config.OUTPUT_VARIABLE = outputVariable || "";
log("info", "配置解析成功 - 方式三", config);
}
}
// 验证必要的配置参数
if (!config.DIFY_API_URL || !config.API_KEY || !config.BOT_TYPE) {
log("error", "缺少必要的配置参数", {
DIFY_API_URL: !!config.DIFY_API_URL,
API_KEY: !!config.API_KEY,
BOT_TYPE: !!config.BOT_TYPE,
config
});
throw new Error("Missing required configuration parameters");
}
return config;
}
const app = express();
// 配置 CORS 中间件,允许所有跨域请求
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "*");
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Max-Age", "86400");
if (req.method === "OPTIONS") {
return res.sendStatus(200);
}
next();
});
// 配置静态文件服务
app.use(express.static('public'));
// 配置请求体解析
app.use(express.json({ limit: "100mb" }));
app.use(express.urlencoded({ limit: "100mb", extended: true }));
app.use(express.raw({ limit: "100mb" }));
// 添加请求体日志(仅开发环境)
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'development' && req.method === "POST") {
log('debug', 'Raw request body', { body: req.body });
}
next();
});
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'development') {
log('info', 'Incoming request', {
method: req.method,
path: req.path
});
}
next();
});
// 根路径
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
// 获取模型列表
app.get("/v1/models", (req, res) => {
const models = {
object: "list",
data: [
{
id: "dify",
object: "model",
owned_by: "dify",
permission: null,
capabilities: {
vision: true,
file_processing: true,
},
},
],
};
res.json(models);
});
// 处理 /v1/chat/completions 请求
app.post("/v1/chat/completions", async (req, res) => {
const requestId = generateId();
const startTime = Date.now();
// 记录请求详情
logRequest(req, requestId);
const authHeader = req.headers.authorization;
if (!authHeader) {
const error = new Error("Missing Authorization header");
log("error", "缺少 Authorization header", {
requestId,
error: error.message,
stack: error.stack,
});
return res.status(401).json({ error: "Missing Authorization header" });
}
try {
// 解析配置
const config = parseConfig(authHeader, req.body.model);
const botType = config.BOT_TYPE;
log("debug", "请求参数处理", {
requestId,
botType,
});
// 根据 botType 分发请求
if (botType === "Chat") {
await chatHandler.handleRequest(req, res, config, requestId, startTime);
} else if (botType === "Completion") {
await completionHandler.handleRequest(
req,
res,
config,
requestId,
startTime
);
} else if (botType === "Workflow") {
await workflowHandler.handleRequest(
req,
res,
config,
requestId,
startTime
);
} else {
log("error", "无效的 bot 类型", { botType });
throw new Error("Invalid bot type in configuration.");
}
} catch (error) {
// 详细记录错误信息
log("error", "处理请求时发生错误", {
requestId,
error: {
message: error.message,
stack: error.stack,
name: error.name,
},
timestamp: new Date().toISOString(),
});
res.status(500).json({ error: error.message });
}
});
const server = http.createServer(app);
server.listen(process.env.PORT || 3099, () => {
log('info', '服务器启动成功', {
port: process.env.PORT || 3099,
env: process.env.NODE_ENV || 'development'
});
});