119 lines
3.3 KiB
JavaScript
119 lines
3.3 KiB
JavaScript
const axios = require("axios");
|
||
const baseModel = require("../../middleware/baseModel");
|
||
const config = require("../../config/config");
|
||
const logs = require("../../tool/logs_proxy");
|
||
|
||
const upstreamBaseUrl = config.upstream_api_url || "http://127.0.0.1:8888";
|
||
|
||
/** 写入日志用:序列化响应并截断,避免 TEXT 过大 */
|
||
const RESPONSE_BODY_MAX_LEN = 16000;
|
||
|
||
function serialize_response_for_log(data) {
|
||
if (data === undefined || data === null) return "";
|
||
let s;
|
||
if (typeof data === "string") s = data;
|
||
else {
|
||
try {
|
||
s = JSON.stringify(data);
|
||
} catch (e) {
|
||
s = String(data);
|
||
}
|
||
}
|
||
if (s.length > RESPONSE_BODY_MAX_LEN) {
|
||
return `${s.slice(0, RESPONSE_BODY_MAX_LEN)}…[truncated]`;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/**
|
||
* 转发请求到上游并记录调用日志
|
||
* @param {object} params
|
||
* @param {string} params.api_path - 接口路径,如 /user/GetProfile
|
||
* @param {string} params.method - HTTP 方法
|
||
* @param {object} params.query - query 参数(透传)
|
||
* @param {object} params.body - body 参数(透传)
|
||
* @param {object} params.headers - 需要透传的请求头
|
||
* @param {object} params.auth_ctx - 鉴权上下文(verifyRequest 返回的 context)
|
||
* @returns {object} { status, data, headers }
|
||
*/
|
||
async function forwardRequest({ api_path, method, query, body, headers, auth_ctx = {} }) {
|
||
const url = `${upstreamBaseUrl}${api_path}`;
|
||
const start = Date.now();
|
||
let status_code = 0;
|
||
let resp_data = null;
|
||
let resp_headers = {};
|
||
|
||
try {
|
||
const forwardHeaders = {};
|
||
if (headers["content-type"]) forwardHeaders["content-type"] = headers["content-type"];
|
||
if (headers["user-agent"]) forwardHeaders["user-agent"] = headers["user-agent"];
|
||
|
||
const resp = await axios({
|
||
method: method.toLowerCase(),
|
||
url,
|
||
params: query,
|
||
data: body,
|
||
headers: forwardHeaders,
|
||
timeout: 30000,
|
||
validateStatus: () => true,
|
||
});
|
||
|
||
status_code = resp.status;
|
||
resp_data = resp.data;
|
||
resp_headers = resp.headers;
|
||
} catch (err) {
|
||
status_code = 502;
|
||
resp_data = { ok: false, error_code: "UPSTREAM_ERROR", message: err.message };
|
||
logs.error(`[proxy] 转发失败 ${api_path}`, err.message);
|
||
}
|
||
|
||
const response_time = Date.now() - start;
|
||
|
||
// 异步写调用日志,不阻塞响应
|
||
writeCallLog({
|
||
user_id: auth_ctx.user_id,
|
||
token_id: auth_ctx.token_id,
|
||
api_path,
|
||
http_method: method.toUpperCase(),
|
||
status_code,
|
||
response_time,
|
||
response_body: serialize_response_for_log(resp_data),
|
||
}).catch((e) => logs.error("[proxy] 写调用日志失败", e.message));
|
||
|
||
return { status: status_code, data: resp_data, headers: resp_headers };
|
||
}
|
||
|
||
/**
|
||
* 写入 API 调用日志
|
||
*/
|
||
async function writeCallLog({
|
||
user_id,
|
||
token_id,
|
||
api_path,
|
||
http_method,
|
||
status_code,
|
||
response_time,
|
||
response_body,
|
||
}) {
|
||
try {
|
||
const now = new Date();
|
||
const call_date = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
||
await baseModel.biz_api_call_log.create({
|
||
user_id,
|
||
token_id,
|
||
api_path,
|
||
http_method,
|
||
status_code,
|
||
response_time,
|
||
response_body: response_body || null,
|
||
call_date,
|
||
created_at: now,
|
||
});
|
||
|
||
} catch (e) {
|
||
logs.error("[proxy] 写调用日志失败", e.message);
|
||
}
|
||
}
|
||
|
||
module.exports = { forwardRequest };
|