修复aiyifan-util.js中部分语法在ios fw中不适配的问题

This commit is contained in:
huangxd- committed 2026-03-31 23:49:34 +08:00
1 parent c2eeeb164b
commit c049d1c312
1 file changed
+140 -49
+140 -49
View File
@@ -12,8 +12,34 @@ const DEFAULT_USER_AGENT = (
export const AIYIFAN_SIGNING_CONFIG_TTL_MS = 60 * 1000;
// 安全获取对象属性(替代可选链操作符)
function safeGet(obj, path, defaultValue) {
if (obj == null) return defaultValue;
const keys = path.split('.');
let result = obj;
for (let i = 0; i < keys.length; i++) {
if (result == null) return defaultValue;
// 处理数组索引,如 config[0]
const key = keys[i];
const arrayMatch = key.match(/^(.+)\[(\d+)\]$/);
if (arrayMatch) {
const arrKey = arrayMatch[1];
const index = parseInt(arrayMatch[2], 10);
result = result[arrKey];
if (Array.isArray(result) && index < result.length) {
result = result[index];
} else {
return defaultValue;
}
} else {
result = result[key];
}
}
return result !== undefined ? result : defaultValue;
}
function extractAssignedObjectLiteral(html, variableName) {
const assignmentPattern = new RegExp(`\\b(?:var|let|const)\\s+${variableName}\\s*=\\s*`);
const assignmentPattern = new RegExp('\\b(?:var|let|const)\\s+' + variableName + '\\s*=\\s*');
const match = assignmentPattern.exec(html);
if (!match) {
return null;
@@ -74,8 +100,8 @@ function parseFallbackPConfig(html) {
let privateKeys = [];
try {
privateKeys = JSON.parse(`[${match[2]}]`);
} catch {
privateKeys = JSON.parse('[' + match[2] + ']');
} catch (e) {
return null;
}
@@ -90,9 +116,14 @@ function parseFallbackPConfig(html) {
}
export function extractPConfigFromInjectJson(injectJson) {
const config = injectJson?.config?.[0]?.pConfig;
const publicKey = config?.publicKey;
const privateKey = Array.isArray(config?.privateKey) ? config.privateKey[0] : config?.privateKey;
// 使用安全获取替代可选链
const config = safeGet(injectJson, 'config[0]', null);
const pConfig = config ? config.pConfig : null;
if (!pConfig) return null;
const publicKey = pConfig.publicKey;
const privateKey = Array.isArray(pConfig.privateKey) ? pConfig.privateKey[0] : pConfig.privateKey;
if (!publicKey || !privateKey) {
return null;
@@ -111,7 +142,7 @@ export function extractPConfigFromHtml(html) {
return signingConfig;
}
} catch (error) {
log("warn", `[Aiyifan] 解析 injectJson 失败,回退到 pConfig 提取: ${error.message}`);
log("warn", '[Aiyifan] 解析 injectJson 失败,回退到 pConfig 提取: ' + (error.message || '未知错误'));
}
}
@@ -137,12 +168,22 @@ function splitQueryString(queryString) {
return queryString
.split("&")
.filter(Boolean)
.map(pair => {
.map(function(pair) {
const equalsIndex = pair.indexOf("=");
const rawKey = equalsIndex === -1 ? pair : pair.slice(0, equalsIndex);
const rawValue = equalsIndex === -1 ? "" : pair.slice(equalsIndex + 1);
const key = decodeURIComponent(rawKey.replace(/\+/g, "%20"));
const value = decodeURIComponent(rawValue.replace(/\+/g, "%20"));
// 手动解码,替代 decodeURIComponent 的异常处理
function safeDecode(str) {
try {
return decodeURIComponent(str.replace(/\+/g, "%20"));
} catch (e) {
return str;
}
}
const key = safeDecode(rawKey);
const value = safeDecode(rawValue);
return [key, value];
});
}
@@ -163,32 +204,63 @@ function getQueryEntries(input) {
if (queryIndex !== -1) {
const hashIndex = trimmed.indexOf("#", queryIndex);
queryString = trimmed.slice(queryIndex + 1, hashIndex === -1 ? undefined : hashIndex);
} else if (trimmed.startsWith("?")) {
} else if (trimmed.charAt(0) === "?") {
queryString = trimmed.slice(1);
}
return splitQueryString(queryString);
}
if (input instanceof URLSearchParams) {
return Array.from(input.entries());
// 移除 URLSearchParams 检查,统一按普通对象处理
if (typeof input === "object" && input !== null) {
// 如果 input 有 entries 方法且返回数组,使用它
if (typeof input.entries === "function") {
try {
var entries = input.entries();
if (Array.isArray(entries)) {
return entries;
}
// 处理迭代器情况
var result = [];
// 尝试作为 Map 或类似对象处理
if (typeof input.forEach === "function") {
input.forEach(function(value, key) {
result.push([key, normalizeQueryValue(value)]);
});
return result.filter(function(item) {
return item[1] !== null;
});
}
} catch (e) {
// 失败则回退到 Object.entries
}
}
// 标准对象处理
return Object.keys(input).map(function(key) {
return [key, normalizeQueryValue(input[key])];
}).filter(function(item) {
return item[1] !== null;
});
}
return Object.entries(input)
.map(([key, value]) => [key, normalizeQueryValue(value)])
.filter(([, value]) => value !== null);
return [];
}
export function buildCanonicalQuery(input) {
return getQueryEntries(input)
.filter(([key]) => !isSigningParam(key))
.map(([key, value]) => `${key}=${value}`)
.filter(function(item) {
return !isSigningParam(item[0]);
})
.map(function(item) {
return item[0] + "=" + item[1];
})
.join("&");
}
export function computeAiyifanVv(input, signingConfig) {
const query = buildCanonicalQuery(input);
const raw = `${signingConfig.publicKey}&${query.toLowerCase()}&${signingConfig.privateKey}`;
const raw = signingConfig.publicKey + "&" + query.toLowerCase() + "&" + signingConfig.privateKey;
return md5(raw);
}
@@ -200,89 +272,108 @@ function normalizeJsonPayload(data) {
}
function isSignedRequestSuccessful(payload) {
return payload?.ret === 200 && payload?.data?.code === 0;
// 使用安全获取替代可选链
var ret = safeGet(payload, 'ret', null);
var code = safeGet(payload, 'data.code', null);
return ret === 200 && code === 0;
}
function getFailureMessage(payload, status) {
return payload?.data?.msg || payload?.msg || `HTTP ${status}`;
// 使用安全获取替代可选链
var msg = safeGet(payload, 'data.msg', null) || safeGet(payload, 'msg', null);
return msg || ('HTTP ' + status);
}
export class AiyifanSigningProvider {
constructor(options = {}) {
this.request = options.request || httpGet;
this.proxyUrlBuilder = options.proxyUrlBuilder || (url => globals.makeProxyUrl(url));
constructor(options) {
options = options || {};
this.proxyUrlBuilder = options.proxyUrlBuilder || function(url) {
return globals.makeProxyUrl(url);
};
this.userAgent = options.userAgent || DEFAULT_USER_AGENT;
this.configPageUrl = options.configPageUrl || DEFAULT_CONFIG_PAGE_URL;
this.ttlMs = options.ttlMs || AIYIFAN_SIGNING_CONFIG_TTL_MS;
this.now = options.now || (() => Date.now());
this.now = options.now || function() { return Date.now(); };
this.signingConfig = null;
this.signingConfigFetchedAt = 0;
}
async getSigningConfig(forceRefresh = false) {
const now = this.now();
const cacheValid = this.signingConfig && (now - this.signingConfigFetchedAt) < this.ttlMs;
async getSigningConfig(forceRefresh) {
forceRefresh = forceRefresh || false;
var now = this.now();
var cacheValid = this.signingConfig && (now - this.signingConfigFetchedAt) < this.ttlMs;
if (!forceRefresh && cacheValid) {
return this.signingConfig;
}
const response = await this.request(this.proxyUrlBuilder(this.configPageUrl), {
var response = await httpGet(this.proxyUrlBuilder(this.configPageUrl), {
headers: {
"User-Agent": this.userAgent,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
}
});
const html = typeof response.data === "string" ? response.data : String(response.data ?? "");
const signingConfig = extractPConfigFromHtml(html);
var html = typeof response.data === "string" ? response.data : String(response.data || "");
var signingConfig = extractPConfigFromHtml(html);
if (!signingConfig) {
throw new Error("未能从桌面站页面解析到 pConfig");
}
this.signingConfig = signingConfig;
this.signingConfigFetchedAt = now;
log("info", `[Aiyifan] 已更新桌面站签名配置: ${signingConfig.publicKey.slice(0, 12)}...`);
log("info", '[Aiyifan] 已更新桌面站签名配置: ' + signingConfig.publicKey.slice(0, 12) + '...');
return signingConfig;
}
buildSignedParams(baseParams, signingConfig) {
return {
...baseParams,
vv: computeAiyifanVv(baseParams, signingConfig),
pub: signingConfig.publicKey
};
var result = {};
// 手动复制对象,替代展开运算符
for (var key in baseParams) {
if (baseParams.hasOwnProperty(key)) {
result[key] = baseParams[key];
}
}
result.vv = computeAiyifanVv(baseParams, signingConfig);
result.pub = signingConfig.publicKey;
return result;
}
async signedGetJson(api, baseParams, headers = {}, logPrefix = "Aiyifan", forceRefresh = false) {
const signingConfig = await this.getSigningConfig(forceRefresh);
const signedParams = this.buildSignedParams(baseParams, signingConfig);
const requestUrl = updateQueryString(api, signedParams);
const response = await this.request(this.proxyUrlBuilder(requestUrl), { headers });
async signedGetJson(api, baseParams, headers, logPrefix, forceRefresh) {
headers = headers || {};
logPrefix = logPrefix || "Aiyifan";
forceRefresh = forceRefresh || false;
var signingConfig = await this.getSigningConfig(forceRefresh);
var signedParams = this.buildSignedParams(baseParams, signingConfig);
var requestUrl = updateQueryString(api, signedParams);
var response = await httpGet(this.proxyUrlBuilder(requestUrl), { headers: headers });
let payload;
var payload;
try {
payload = normalizeJsonPayload(response.data);
} catch (error) {
if (!forceRefresh) {
log("warn", `[${logPrefix}] 响应无法解析为 JSON,刷新签名配置后重试: ${error.message}`);
log("warn", '[' + logPrefix + '] 响应无法解析为 JSON,刷新签名配置后重试: ' + (error.message || '未知错误'));
return this.signedGetJson(api, baseParams, headers, logPrefix, true);
}
throw error;
}
if (response.status !== 200 || !isSignedRequestSuccessful(payload)) {
// 安全获取状态码,如果不存在则默认为200
var statusCode = response.status != null ? response.status : 200;
if (statusCode !== 200 || !isSignedRequestSuccessful(payload)) {
if (!forceRefresh) {
log("warn", `[${logPrefix}] 当前签名请求失败,刷新 pConfig 后重试: ${getFailureMessage(payload, response.status)}`);
log("warn", '[' + logPrefix + '] 当前签名请求失败,刷新 pConfig 后重试: ' + getFailureMessage(payload, statusCode));
return this.signedGetJson(api, baseParams, headers, logPrefix, true);
}
throw new Error(getFailureMessage(payload, response.status));
throw new Error(getFailureMessage(payload, statusCode));
}
return {
data: payload,
vv: signedParams.vv,
signingConfig
signingConfig: signingConfig
};
}
}
}