92 lines
2.5 KiB
JavaScript
92 lines
2.5 KiB
JavaScript
const swagger = require("../../_docs/swagger.json");
|
||
const auth = require("../service/biz_auth_verify");
|
||
const proxy = require("../service/biz_proxy_service");
|
||
|
||
function getRequestBody(ctx) {
|
||
if (ctx.request && ctx.request.body && Object.keys(ctx.request.body).length > 0) {
|
||
return ctx.request.body;
|
||
}
|
||
if (typeof ctx.getBody === "function") {
|
||
return ctx.getBody() || {};
|
||
}
|
||
return {};
|
||
}
|
||
|
||
/**
|
||
* 从请求中提取 Token
|
||
* 支持 Authorization: Bearer xxx 和 query ?token=xxx
|
||
*/
|
||
function extractToken(ctx) {
|
||
const authHeader = ctx.get("Authorization") || "";
|
||
if (authHeader.startsWith("Bearer ")) {
|
||
return authHeader.slice(7).trim();
|
||
}
|
||
return ctx.query.token || "";
|
||
}
|
||
|
||
/**
|
||
* 提取 swagger tags 第一项作为 feature 名(用于套餐功能点校验)
|
||
*/
|
||
function pickFeature(spec) {
|
||
if (spec.tags && spec.tags.length > 0) {
|
||
return spec.tags[0];
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 构建转发路由表(供 framework.addRoutes 注册)
|
||
*/
|
||
function buildProxyRoutes() {
|
||
const routes = {};
|
||
|
||
for (const [path, methods] of Object.entries(swagger.paths)) {
|
||
for (const [method, spec] of Object.entries(methods)) {
|
||
const routeKey = `${method.toUpperCase()} ${path}`;
|
||
|
||
routes[routeKey] = async (ctx) => {
|
||
// 1. 提取 Token
|
||
const token = extractToken(ctx);
|
||
if (!token) {
|
||
ctx.fail("缺少 Token");
|
||
return;
|
||
}
|
||
|
||
// 2. 鉴权:Token + 用户 + 订阅 + 套餐功能点 + 接口权限 + 调用量
|
||
const feature = pickFeature(spec);
|
||
const authResult = await auth.verifyRequest({ token, feature, api_path: path });
|
||
if (!authResult.ok) {
|
||
ctx.fail(authResult.message || "鉴权失败");
|
||
return;
|
||
}
|
||
|
||
// 3. 组装 query(去掉 token 参数,避免泄露)
|
||
const query = { ...ctx.query };
|
||
delete query.token;
|
||
|
||
// 4. 转发到上游
|
||
const result = await proxy.forwardRequest({
|
||
api_path: path,
|
||
method: method.toUpperCase(),
|
||
query,
|
||
body: getRequestBody(ctx),
|
||
headers: ctx.headers || {},
|
||
auth_ctx: authResult.context,
|
||
});
|
||
|
||
// 5. 根据上游 Success 字段决定响应方式
|
||
const upstream = result.data;
|
||
if (upstream && upstream.Success === true) {
|
||
ctx.success(upstream);
|
||
} else {
|
||
ctx.fail(upstream && upstream.Text ? upstream.Text : "上游请求失败", upstream);
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
return routes;
|
||
}
|
||
|
||
module.exports = { buildProxyRoutes };
|