Files
wechatWeb/api/controller_custom/proxy_api.js
张成 09368d2a95 1
2026-04-01 14:16:06 +08:00

96 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const swagger = require("../../_docs/swagger.json");
const auth = require("../service/biz_auth_verify");
const proxy = require("../service/biz_proxy_service");
/**
* 从 ctx 请求头中提取 Token不含 query
* - Authorization: Bearer <token>
* - Authorization: <token>(无 Bearer 前缀时整段作为 token
* - X-Api-Token / X-Token
*/
function extractToken(ctx) {
let x_token = ctx.headers['authorization'] || ''
if (x_token.startsWith("Bearer ")) {
x_token = x_token.slice(7).trim();
}
return x_token;
}
/**
* 提取 swagger tags 第一项作为 feature 名(用于套餐功能点校验)
*/
function pickFeature(spec) {
if (spec.tags && spec.tags.length > 0) {
return spec.tags[0];
}
return null;
}
/** 不参与转发的文档路径(与 framework 实际路由重叠或仅为说明) */
function should_skip_proxy_path(route_path) {
return (
route_path.startsWith("/admin_api") ||
route_path.startsWith("/api/auth")
);
}
/**
* 构建转发路由表(供 framework.addRoutes 注册)
*/
function buildProxyRoutes() {
const routes = {};
for (const [path, methods] of Object.entries(swagger.paths)) {
if (should_skip_proxy_path(path)) {
continue;
}
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
const query = { ...ctx.query };
// 4. 转发到上游
const result = await proxy.forwardRequest({
api_path: path,
method: method.toUpperCase(),
query,
body: ctx.getBody(),
headers: ctx.headers || {},
auth_ctx: authResult.context,
});
// 5. 根据上游 Success 字段决定响应方式
const upstream = result.data;
if (upstream && upstream.Code === 200) {
ctx.success(upstream);
} else {
ctx.fail(upstream && upstream.Text ? upstream.Text : "上游请求失败", upstream);
}
};
}
}
return routes;
}
module.exports = { buildProxyRoutes };