mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-25 10:28:37 +00:00
7.4 KiB
7.4 KiB
JavaScript Runtime Scripts
本目录包含 JavaScript Runtime 中间件使用的脚本文件。
脚本加载
- 系统会自动读取
scripts/目录下的所有.js文件 - 脚本按文件名字母顺序加载
- 建议使用数字前缀来控制加载顺序(如:
01_utils.js,02_pre_process.js) - 所有脚本会被合并到一个 JavaScript 运行时环境中
配置
通过环境变量配置:
JS_RUNTIME_ENABLED=true- 启用 JavaScript RuntimeJS_SCRIPT_DIR=scripts/- 脚本目录路径JS_MAX_VM_COUNT=8- 最大虚拟机数量JS_SCRIPT_TIMEOUT=5s- 脚本执行超时时间JS_FETCH_TIMEOUT=10s- HTTP 请求超时时间
更多的详细配置可以在 .env.example 文件中找到,并在实际使用时重命名为 .env。
必需的函数
脚本中必须定义以下两个函数:
1. preProcessRequest(req)
在请求被转发到后端 API 之前调用。
参数:
req: 请求对象,包含method,url,headers,body等属性
返回值: 返回一个对象,可包含以下属性:
block: boolean - 是否阻止请求继续执行statusCode: number - 阻止请求时返回的状态码message: string - 阻止请求时返回的错误消息headers: object - 要修改或添加的请求头body: any - 修改后的请求体
2. postProcessResponse(req, res)
在响应返回给客户端之前调用。
参数:
req: 原始请求对象res: 响应对象,包含statusCode,headers,body等属性
返回值: 返回一个对象,可包含以下属性:
statusCode: number - 修改后的状态码headers: object - 要修改或添加的响应头body: string - 修改后的响应体
可用的全局对象和函数
console.log(),console.error(),console.warn()- 日志输出JSON.parse(),JSON.stringify()- JSON 处理fetch(url, options)- HTTP 请求db- 主数据库连接logdb- 日志数据库连接setTimeout(fn, delay)- 定时器
示例脚本
参考现有的示例脚本:
01_utils.js- 工具函数02_pre_process.js- 请求预处理03_post_process.js- 响应后处理
使用示例
// 例子:基于数据库的速率限制
if (req.url.includes("/v1/chat/completions")) {
try {
// Check recent requests from this IP
var recentRequests = db.query(
"SELECT COUNT(*) as count FROM logs WHERE created_at > ? AND ip = ?",
Math.floor(Date.now() / 1000) - 60, // last minute
req.remoteIP
);
if (recentRequests && recentRequests.length > 0 && recentRequests[0].count > 10) {
console.log("速率限制 IP:", req.remoteIP);
return {
block: true,
statusCode: 429,
message: "超过速率限制"
};
}
} catch (e) {
console.error("Ratelimit 数据库错误:", e);
}
}
// 例子:修改请求
if (req.url.includes("/chat/completions")) {
try {
var bodyObj = req.body;
let firstMsg = { // 需要新建一个对象,不能修改原有对象
role: "user",
content: "喵呜🐱~嘻嘻"
};
bodyObj.messages[0] = firstMsg;
console.log("Modified first message:", JSON.stringify(firstMsg));
console.log("Modified body:", JSON.stringify(bodyObj));
return {
body: bodyObj,
headers: {
...req.headers,
"X-Modified-Body": "true"
}
};
} catch (e) {
console.error("Failed to modify request body:", {
message: e.message,
stack: e.stack,
bodyType: typeof req.body,
url: req.url
});
}
}
// 例子:读取最近一条日志,新增 jsrt 日志,并输出日志总数
try {
// 1. 读取最近一条日志
var recentLogs = logdb.query(
"SELECT id, user_id, username, content, created_at FROM logs ORDER BY id DESC LIMIT 1"
);
var recentLog = null;
if (recentLogs && recentLogs.length > 0) {
recentLog = recentLogs[0];
console.log("最近一条日志:", JSON.stringify(recentLog));
}
// 2. 新增一条 jsrt 日志
var currentTimestamp = Math.floor(Date.now() / 1000);
var jsrtLogContent = "JSRT 预处理中间件执行 - " + req.URL + " - " + new Date().toISOString();
var insertResult = logdb.exec(
"INSERT INTO logs (user_id, username, created_at, type, content) VALUES (?, ?, ?, ?, ?)",
req.UserID || 0,
req.Username || "jsrt-system",
currentTimestamp,
4, // LogTypeSystem
jsrtLogContent
);
if (insertResult.error) {
console.error("插入 JSRT 日志失败:", insertResult.error);
} else {
console.log("成功插入 JSRT 日志,影响行数:", insertResult.rowsAffected);
}
// 3. 输出日志总数
var totalLogsResult = logdb.query("SELECT COUNT(*) as total FROM logs");
var totalLogs = 0;
if (totalLogsResult && totalLogsResult.length > 0) {
totalLogs = totalLogsResult[0].total;
}
console.log("当前日志总数:", totalLogs);
console.log("JSRT 日志管理示例执行完成");
} catch (e) {
console.error("JSRT 日志管理示例执行失败:", {
message: e.message,
stack: e.stack,
url: req.URL
});
}
// 例子:使用 fetch 调用外部 API
if (req.url.includes("/api/uptime/status")) {
try {
// 使用 httpbin.org/ip 测试 fetch 功能
var response = fetch("https://httpbin.org/ip", {
method: "GET",
timeout: 5, // 5秒超时
headers: {
"User-Agent": "OneAPI-JSRT/1.0"
}
});
if (response.Error.length === 0) {
// 解析响应体
var ipData = JSON.parse(response.Body);
// 可以根据获取到的 IP 信息进行后续处理
if (ipData.origin) {
console.log("外部 IP 地址:", ipData.origin);
// 示例:记录 IP 信息到数据库
var currentTimestamp = Math.floor(Date.now() / 1000);
var logContent = "Fetch 示例 - 外部 IP: " + ipData.origin + " - " + new Date().toISOString();
var insertResult = logdb.exec(
"INSERT INTO logs (user_id, username, created_at, type, content) VALUES (?, ?, ?, ?, ?)",
0,
"jsrt-fetch",
currentTimestamp,
4, // LogTypeSystem
logContent
);
if (insertResult.error) {
console.error("记录 IP 信息失败:", insertResult.error);
} else {
console.log("成功记录 IP 信息到数据库");
}
}
} else {
console.error("Fetch 失败 ", response.Status, " ", response.Error);
}
} catch (e) {
console.error("Fetch 失败:", {
message: e.message,
stack: e.stack,
url: req.url
});
}
}
管理接口
重新加载脚本
curl -X POST http://host:port/api/jsrt/reload \
-H 'Content-Type: application/json' \
-H 'Authorization Bearer <admin_token>'
故障排除
- 查看服务日志中的 JavaScript 相关错误信息
- 使用
console.log()调试脚本逻辑 - 确保 JavaScript 语法正确(不支持所有 ES6+ 特性)