Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
143 lines
5.0 KiB
JavaScript
143 lines
5.0 KiB
JavaScript
/**
|
||
* 前端请求封装。
|
||
* - 登录态走 cookie(credentials: include)
|
||
* - 浏览器无法改写 User-Agent;X-Real-IP 由服务端反代上游时注入
|
||
* - 直连上游时若需自定义头,请走本站 Go 代理接口
|
||
*/
|
||
(function (global) {
|
||
var DEFAULT_TIMEOUT_MS = 30000;
|
||
|
||
function buildQuery(params) {
|
||
if (!params) return "";
|
||
var pairs = [];
|
||
Object.keys(params).forEach(function (key) {
|
||
var value = params[key];
|
||
if (value == null) return;
|
||
pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(value)));
|
||
});
|
||
return pairs.length ? "?" + pairs.join("&") : "";
|
||
}
|
||
|
||
/**
|
||
* @param {string} url
|
||
* @param {{
|
||
* method?: string,
|
||
* data?: any,
|
||
* params?: Record<string, any>,
|
||
* headers?: Record<string, string>,
|
||
* timeout?: number,
|
||
* }} [options]
|
||
*/
|
||
function request(url, options) {
|
||
options = options || {};
|
||
var method = String(options.method || "GET").toUpperCase();
|
||
var headers = Object.assign(
|
||
{ Accept: "application/json" },
|
||
options.headers || {}
|
||
);
|
||
var finalUrl = url + buildQuery(options.params);
|
||
var body;
|
||
var controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
||
var timer;
|
||
|
||
if (options.data != null && method !== "GET" && method !== "HEAD") {
|
||
if (typeof FormData !== "undefined" && options.data instanceof FormData) {
|
||
body = options.data;
|
||
} else {
|
||
headers["Content-Type"] = headers["Content-Type"] || "application/json";
|
||
body =
|
||
headers["Content-Type"].indexOf("application/json") >= 0
|
||
? JSON.stringify(options.data)
|
||
: options.data;
|
||
}
|
||
}
|
||
|
||
var fetchOpts = {
|
||
method: method,
|
||
headers: headers,
|
||
credentials: "include",
|
||
signal: controller ? controller.signal : undefined,
|
||
};
|
||
if (body != null) fetchOpts.body = body;
|
||
|
||
var timeoutMs =
|
||
typeof options.timeout === "number" ? options.timeout : DEFAULT_TIMEOUT_MS;
|
||
if (controller && timeoutMs > 0) {
|
||
timer = setTimeout(function () {
|
||
controller.abort();
|
||
}, timeoutMs);
|
||
}
|
||
|
||
return fetch(finalUrl, fetchOpts)
|
||
.then(function (res) {
|
||
var contentType = res.headers.get("content-type") || "";
|
||
var parse = contentType.indexOf("application/json") >= 0
|
||
? res.json()
|
||
: res.text();
|
||
return parse.then(function (data) {
|
||
if (!res.ok) {
|
||
var err = new Error(
|
||
(data && data.msg) || (data && data.message) || res.statusText || "request failed"
|
||
);
|
||
err.status = res.status;
|
||
err.code = data && data.code;
|
||
err.data = data;
|
||
throw err;
|
||
}
|
||
return data;
|
||
});
|
||
})
|
||
.then(function (data) {
|
||
return handleTokenExpiredResponse(url, options, data);
|
||
})
|
||
.finally(function () {
|
||
if (timer) clearTimeout(timer);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 业务码 1000:token 失效。
|
||
* 游客 → 重新游客登录并重试一次;注册账号 → 打开登录弹窗/跳转登录页。
|
||
*/
|
||
function handleTokenExpiredResponse(url, options, data) {
|
||
options = options || {};
|
||
var expiredCode =
|
||
(global.AuthAPI && global.AuthAPI.TOKEN_EXPIRED_CODE) || 1000;
|
||
if (!data || Number(data.code) !== Number(expiredCode)) {
|
||
return data;
|
||
}
|
||
if (options._skipTokenRecovery || options._tokenRetried) {
|
||
return data;
|
||
}
|
||
if (
|
||
global.AuthAPI &&
|
||
typeof global.AuthAPI.isAuthEndpoint === "function" &&
|
||
global.AuthAPI.isAuthEndpoint(url)
|
||
) {
|
||
return data;
|
||
}
|
||
if (!global.AuthAPI || typeof global.AuthAPI.handleTokenExpired !== "function") {
|
||
return data;
|
||
}
|
||
return global.AuthAPI.handleTokenExpired().then(function (result) {
|
||
if (result && result.ok && result.guest) {
|
||
return request(
|
||
url,
|
||
Object.assign({}, options, { _tokenRetried: true })
|
||
);
|
||
}
|
||
return data;
|
||
});
|
||
}
|
||
|
||
global.HttpRequest = {
|
||
request: request,
|
||
get: function (url, options) {
|
||
return request(url, Object.assign({}, options, { method: "GET" }));
|
||
},
|
||
post: function (url, data, options) {
|
||
return request(url, Object.assign({}, options, { method: "POST", data: data }));
|
||
},
|
||
};
|
||
})(typeof window !== "undefined" ? window : this);
|