hyapp-h5/common_aslan/aslan-common.js
2026-07-09 19:18:58 +08:00

524 lines
16 KiB
JavaScript
Raw Permalink 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.

(function () {
'use strict';
var PROD_API_BASE = 'https://api.atuchat.com/';
var LOCAL_API_BASE = 'http://127.0.0.1:1100/';
var APP_CODE = 'aslan';
var DEFAULT_ORIGIN = 'ATYOU';
var readyPromise = null;
var headers = {
authorization: '',
'req-app-intel':
'version=1.0.0;build=1;model=H5;sysVersion=web;channel=Web',
'req-client': 'H5',
'req-imei': 'H5-STATIC',
'req-lang': 'en',
'req-sys-origin': 'origin=ATYOU;originChild=ATYOU',
'req-version': 'V2',
'req-zone': 'Asia/Shanghai',
};
function currentParams() {
var params = new URLSearchParams(window.location.search || '');
var hash = window.location.hash || '';
if (hash.indexOf('?') >= 0) {
new URLSearchParams(hash.split('?').slice(1).join('?')).forEach(
function (value, key) {
if (!params.has(key)) params.set(key, value);
}
);
}
return params;
}
function query(name) {
return currentParams().get(name);
}
function normalizeBaseURL(value) {
return String(value || '').replace(/\/+$/, '');
}
function resolveEnv() {
var env = String(query('env') || '')
.trim()
.toLowerCase();
return env === 'local' || env === 'test' || env === 'dev' ? env : '';
}
function baseURL() {
var override =
query('apiBase') ||
query('api_base') ||
query('aslan_api_base') ||
window.ASLAN_API_BASE_URL ||
(window.AslanCommonConfig && window.AslanCommonConfig.apiBase);
if (override) return normalizeBaseURL(override);
return resolveEnv() === 'local'
? normalizeBaseURL(LOCAL_API_BASE)
: normalizeBaseURL(PROD_API_BASE);
}
function buildURL(path, queryData) {
var url = new URL(
String(path || '').replace(/^\/+/, ''),
baseURL() + '/'
);
Object.keys(queryData || {}).forEach(function (key) {
var value = queryData[key];
if (value === undefined || value === null || value === '') return;
url.searchParams.set(key, value);
});
return url.toString();
}
function normalizeAuthorization(value) {
var text = String(value || '').trim();
if (!text) return '';
return /^Bearer\s+/i.test(text) ? text : 'Bearer ' + text;
}
function splitHeaderPairs(value) {
var result = {};
String(value || '')
.split(';')
.forEach(function (item) {
var index = item.indexOf('=');
if (index > -1) {
result[item.slice(0, index).trim()] = item
.slice(index + 1)
.trim();
}
});
return result;
}
function putHeader(target, key, value) {
if (
value === undefined ||
value === null ||
value === '' ||
target[key]
) {
return;
}
target[key] = String(value);
}
function normalizeHeaderCandidate(value, depth) {
depth = depth || 0;
if (depth > 4 || value === null || value === undefined) return {};
if (typeof value === 'string') {
var trimmed = value.trim();
if (!trimmed) return {};
if (trimmed.charAt(0) === '{' || trimmed.charAt(0) === '[') {
try {
return normalizeHeaderCandidate(
JSON.parse(trimmed),
depth + 1
);
} catch (_) {
return {};
}
}
return { authorization: trimmed };
}
if (Array.isArray(value)) {
return value.reduce(function (result, item) {
return Object.assign(
result,
normalizeHeaderCandidate(item, depth + 1)
);
}, {});
}
if (typeof value !== 'object') return {};
var appIntel = splitHeaderPairs(
value['Req-App-Intel'] || value['req-app-intel']
);
var sysOrigin = splitHeaderPairs(
value['Req-Sys-Origin'] || value['req-sys-origin']
);
var normalized = {};
putHeader(
normalized,
'authorization',
value.Authorization ||
value.authorization ||
value.token ||
value.accessToken
);
putHeader(
normalized,
'reqLang',
value['Req-Lang'] ||
value['req-lang'] ||
value.reqLang ||
value.lang
);
putHeader(
normalized,
'appVersion',
appIntel.version || value.appVersion || value['App-Version']
);
putHeader(
normalized,
'buildVersion',
appIntel.build || value.buildVersion
);
putHeader(normalized, 'model', appIntel.model || value.model);
putHeader(normalized, 'channel', appIntel.channel || value.channel);
putHeader(
normalized,
'reqImei',
appIntel['Req-Imei'] ||
appIntel.imei ||
value['Req-Imei'] ||
value['req-imei'] ||
value.reqImei
);
putHeader(
normalized,
'origin',
sysOrigin.origin || value.sysOrigin || value.origin
);
putHeader(
normalized,
'child',
sysOrigin.originChild ||
sysOrigin.child ||
value.sysOriginChild ||
value.child
);
['headerInfo', 'headers', 'auth', 'data', 'state'].forEach(
function (key) {
Object.assign(
normalized,
normalizeHeaderCandidate(value[key], depth + 1)
);
}
);
return normalized;
}
function setHeadersFromApp(rawOrObject) {
var data = normalizeHeaderCandidate(rawOrObject || {});
var next = {};
if (data.authorization) {
next.authorization = normalizeAuthorization(data.authorization);
}
if (data.reqLang) next['req-lang'] = data.reqLang;
if (
data.appVersion ||
data.buildVersion ||
data.model ||
data.channel
) {
next['req-app-intel'] = [
data.appVersion ? 'version=' + data.appVersion : '',
data.buildVersion ? 'build=' + data.buildVersion : '',
data.model ? 'model=' + data.model : '',
data.channel ? 'channel=' + data.channel : '',
]
.filter(Boolean)
.join(';');
}
if (data.reqImei) next['req-imei'] = data.reqImei;
if (data.origin) {
next['req-sys-origin'] = [
'origin=' + data.origin,
'originChild=' + (data.child || data.origin),
].join(';');
}
headers = Object.assign(headers, next);
return headers;
}
function discoverQueryHeaders() {
return {
authorization:
query('authorization') ||
query('Authorization') ||
query('token') ||
query('accessToken') ||
query('access_token'),
reqLang: query('lang') || query('language') || query('locale'),
origin: query('sysOrigin') || query('origin') || DEFAULT_ORIGIN,
child:
query('sysOriginChild') ||
query('originChild') ||
DEFAULT_ORIGIN,
channel: query('channel') || query('appChannel'),
};
}
function readStorageHeaders(storage) {
var collected = {};
if (!storage) return collected;
[
'authorization',
'Authorization',
'token',
'accessToken',
'headerInfo',
'headersFromApp',
'appHeaders',
'user',
'userInfo',
].forEach(function (key) {
try {
Object.assign(
collected,
normalizeHeaderCandidate(storage.getItem(key))
);
} catch (_) {}
});
return collected;
}
function applyDiscoveredHeaders() {
setHeadersFromApp(discoverQueryHeaders());
setHeadersFromApp(window.__APP_HEADERS__);
setHeadersFromApp(window.__HEADER_INFO__);
setHeadersFromApp(window.headerInfo);
try {
setHeadersFromApp(readStorageHeaders(window.localStorage));
setHeadersFromApp(readStorageHeaders(window.sessionStorage));
} catch (_) {}
}
function ensureAslanAppCode() {
document.documentElement.dataset.hyAppCode = APP_CODE;
var params = currentParams();
if (
params.get('app_code') ||
params.get('app') ||
params.get('appCode') ||
!window.history ||
!window.history.replaceState
) {
return;
}
var url = new URL(window.location.href);
url.searchParams.set('app_code', APP_CODE);
window.history.replaceState(null, '', url.toString());
}
function isAppEnvironment() {
return Boolean(
window.app ||
window.FlutterApp ||
window.FlutterPageControl ||
window.webkit
);
}
function requestAccessOrigin() {
return new Promise(function (resolve) {
var settled = false;
var timeoutId = null;
function finish(raw) {
if (raw) setHeadersFromApp(raw);
if (settled) return;
settled = true;
if (timeoutId) window.clearTimeout(timeoutId);
resolve(raw || null);
}
window.renderData = finish;
window.getIosAccessOriginParam = finish;
if (!isAppEnvironment()) {
finish(null);
return;
}
// Aslan Flutter 在页面完成后注入 window.app.getAccessOrigin这里轮询短时间等待避免首屏请求早于注入而丢登录态。
if (
window.app &&
typeof window.app.getAccessOrigin === 'function'
) {
try {
finish(window.app.getAccessOrigin());
return;
} catch (_) {}
}
if (
window.FlutterApp &&
typeof window.FlutterApp.postMessage === 'function'
) {
try {
window.FlutterApp.postMessage('requestAccessOrigin');
} catch (_) {}
}
var attempt = 0;
var poll = function () {
attempt += 1;
if (
window.app &&
typeof window.app.getAccessOrigin === 'function'
) {
try {
finish(window.app.getAccessOrigin());
return;
} catch (_) {}
}
if (!settled && attempt < 30) window.setTimeout(poll, 80);
};
poll();
timeoutId = window.setTimeout(function () {
finish(null);
}, 2400);
});
}
function connectToApp() {
if (readyPromise) return readyPromise;
ensureAslanAppCode();
applyDiscoveredHeaders();
readyPromise = requestAccessOrigin().then(function () {
return {
baseURL: baseURL(),
headers: Object.assign({}, headers),
authenticated: Boolean(headers.authorization),
};
});
return readyPromise;
}
function parseResponse(response) {
return response.text().then(function (text) {
var payload = text ? JSON.parse(text) : {};
if (!response.ok) {
var httpError = new Error(
payload.message ||
payload.errorMsg ||
response.statusText ||
'request_failed'
);
httpError.status = response.status;
httpError.payload = payload;
throw httpError;
}
if (payload && payload.status === false) {
var apiError = new Error(
payload.message ||
payload.errorMsg ||
payload.error ||
'api_request_failed'
);
apiError.payload = payload;
apiError.code = payload.errorCode || payload.code;
throw apiError;
}
if (
payload &&
Object.prototype.hasOwnProperty.call(payload, 'body')
) {
return payload.body;
}
if (
payload &&
Object.prototype.hasOwnProperty.call(payload, 'data')
) {
return payload.data;
}
return payload;
});
}
function stripAppEncryptedHeaders(target) {
// H5 WebView 只复用 App 登录态,不参与 App Dio 加密;带 Req-Atyou 会让网关按密文解码普通 H5 请求。
Object.keys(target || {}).forEach(function (key) {
if (String(key).toLowerCase() === 'req-atyou') delete target[key];
});
}
function request(path, options) {
var opts = options || {};
var body = opts.body;
if (body && typeof body === 'object' && !(body instanceof FormData)) {
body = JSON.stringify(body);
}
return connectToApp().then(function () {
var requestHeaders = Object.assign({}, headers, opts.headers || {});
stripAppEncryptedHeaders(requestHeaders);
if (!requestHeaders.authorization)
delete requestHeaders.authorization;
if (body) {
requestHeaders['content-type'] =
requestHeaders['content-type'] || 'application/json';
}
return window
.fetch(buildURL(path, opts.query), {
method: opts.method || 'GET',
headers: requestHeaders,
body: body,
credentials: 'omit',
cache: 'no-store',
})
.then(parseResponse);
});
}
function back() {
if (
window.FlutterPageControl &&
window.FlutterPageControl.postMessage
) {
window.FlutterPageControl.postMessage('close_page');
return;
}
if (window.HyAppBridge && window.HyAppBridge.back) {
window.HyAppBridge.back();
return;
}
window.history.back();
}
window.AslanCommon = {
baseURL: baseURL,
buildURL: buildURL,
headers: function () {
return Object.assign({}, headers);
},
connectToApp: connectToApp,
setHeadersFromApp: setHeadersFromApp,
request: request,
get: function (path, queryData) {
return request(path, { method: 'GET', query: queryData });
},
post: function (path, body, queryData) {
return request(path, {
method: 'POST',
body: body,
query: queryData,
});
},
back: back,
user: {
profile: function () {
return request('/user/user-profile/user-id', { method: 'GET' });
},
},
rechargeReward: {
status: function () {
return request(
'/activity/cumulative-recharge/recharge-reward',
{
method: 'GET',
}
);
},
claim: function (ruleId) {
return request('/activity/cumulative-recharge', {
method: 'POST',
body: { id: String(ruleId || '') },
});
},
},
};
})();