fix app invite gateway headers
This commit is contained in:
parent
f42551294f
commit
c27a8acd8f
@ -7,6 +7,14 @@ const ONLINE_INVITE_API_BASE = "https://jvapi.haiyihy.com/";
|
||||
const ONLINE_INVITE_API_PREFIX = "/go";
|
||||
const DEFAULT_AVATAR = "./assets/avatar-sample.png";
|
||||
const DEFAULT_RESET_PERIOD_MS = 30 * 86400 * 1000;
|
||||
const DEFAULT_REQUEST_HEADERS = {
|
||||
"Req-Sys-Origin": "origin=LIKEI;originChild=LIKEI",
|
||||
"Req-Client": "Android",
|
||||
"Req-App-Intel": "version=1.0.0;build=1;channel=Web;model=H5;sysVersion=Web",
|
||||
"Req-Lang": "en",
|
||||
"Req-Version": "V2",
|
||||
"Req-Zone": "Asia/Shanghai",
|
||||
};
|
||||
const DEFAULT_TASK_NOTES = {
|
||||
transport: "(Only the highest-level bonuses may be awarded)",
|
||||
quota: "(You'll receive a reward upon reaching each level)",
|
||||
@ -37,6 +45,7 @@ const state = {
|
||||
apiBase: resolveAPIBase(),
|
||||
apiPrefix: resolveAPIPrefix(),
|
||||
authorization: resolveAuthorization(),
|
||||
requestHeaders: resolveRequestHeaders(),
|
||||
landingCode: resolveLandingCode(),
|
||||
inviteLink: query.get("inviteLink") || DEFAULTS.inviteLink,
|
||||
inviteCode: query.get("inviteCode") || DEFAULTS.inviteCode,
|
||||
@ -122,6 +131,8 @@ function hasAuthorizationInput() {
|
||||
trimValue(query.get("authorization")) ||
|
||||
trimValue(query.get("Authorization")) ||
|
||||
trimValue(query.get("token")) ||
|
||||
trimValue(query.get("token?")) ||
|
||||
trimValue(query.get("accessToken")) ||
|
||||
trimValue(query.get("auth")) ||
|
||||
(typeof window.__APP_INVITE_AUTHORIZATION__ === "string" &&
|
||||
trimValue(window.__APP_INVITE_AUTHORIZATION__)) ||
|
||||
@ -214,6 +225,8 @@ function resolveAuthorization() {
|
||||
query.get("authorization"),
|
||||
query.get("Authorization"),
|
||||
query.get("token"),
|
||||
query.get("token?"),
|
||||
query.get("accessToken"),
|
||||
query.get("auth"),
|
||||
typeof window.__APP_INVITE_AUTHORIZATION__ === "string"
|
||||
? window.__APP_INVITE_AUTHORIZATION__
|
||||
@ -234,6 +247,214 @@ function resolveAuthorization() {
|
||||
return `Bearer ${normalized}`;
|
||||
}
|
||||
|
||||
function resolveRequestHeaders() {
|
||||
const headers = { ...DEFAULT_REQUEST_HEADERS };
|
||||
const sysOrigin = trimValue(query.get("sysOrigin") || query.get("origin"));
|
||||
const sysOriginChild = trimValue(
|
||||
query.get("sysOriginChild") || query.get("originChild") || sysOrigin,
|
||||
);
|
||||
const reqClient = trimValue(query.get("reqClient") || query.get("client"));
|
||||
const reqLang = trimValue(query.get("reqLang") || query.get("lang"));
|
||||
const reqZone = trimValue(query.get("reqZone") || query.get("zone"));
|
||||
const reqVersion = trimValue(query.get("reqVersion") || query.get("version"));
|
||||
const reqImei = trimValue(query.get("reqImei") || query.get("imei"));
|
||||
const appVersion = trimValue(query.get("appVersion")) || "1.0.0";
|
||||
const buildVersion = trimValue(query.get("buildVersion") || query.get("build")) || "1";
|
||||
const appChannel = trimValue(query.get("appChannel") || query.get("channel")) || "Web";
|
||||
|
||||
if (sysOrigin) {
|
||||
headers["Req-Sys-Origin"] = `origin=${sysOrigin};originChild=${sysOriginChild || sysOrigin}`;
|
||||
}
|
||||
if (reqClient) {
|
||||
headers["Req-Client"] = reqClient;
|
||||
}
|
||||
if (reqLang) {
|
||||
headers["Req-Lang"] = reqLang;
|
||||
}
|
||||
if (reqZone) {
|
||||
headers["Req-Zone"] = reqZone;
|
||||
}
|
||||
if (reqVersion) {
|
||||
headers["Req-Version"] = reqVersion;
|
||||
}
|
||||
if (reqImei) {
|
||||
headers["Req-Imei"] = reqImei;
|
||||
}
|
||||
|
||||
headers["Req-App-Intel"] = buildAppIntelHeader({
|
||||
version: appVersion,
|
||||
build: buildVersion,
|
||||
channel: appChannel,
|
||||
model: trimValue(query.get("model")) || "H5",
|
||||
sysVersion: trimValue(query.get("sysVersion")) || "Web",
|
||||
imei: reqImei,
|
||||
});
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function buildAppIntelHeader(parts) {
|
||||
const items = [];
|
||||
const version = trimValue(parts?.version) || "1.0.0";
|
||||
const build = trimValue(parts?.build) || "1";
|
||||
const channel = trimValue(parts?.channel) || "Web";
|
||||
const model = trimValue(parts?.model);
|
||||
const sysVersion = trimValue(parts?.sysVersion);
|
||||
const imei = trimValue(parts?.imei);
|
||||
|
||||
items.push(`version=${version}`);
|
||||
items.push(`build=${build}`);
|
||||
items.push(`channel=${channel}`);
|
||||
if (model) {
|
||||
items.push(`model=${model}`);
|
||||
}
|
||||
if (sysVersion) {
|
||||
items.push(`sysVersion=${sysVersion}`);
|
||||
}
|
||||
if (imei) {
|
||||
items.push(`Req-Imei=${imei}`);
|
||||
}
|
||||
|
||||
return items.join(";");
|
||||
}
|
||||
|
||||
function splitHeaderPairs(value) {
|
||||
const result = {};
|
||||
trimValue(value)
|
||||
.split(";")
|
||||
.forEach((item) => {
|
||||
const index = item.indexOf("=");
|
||||
if (index > -1) {
|
||||
result[item.slice(0, index)] = item.slice(index + 1);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseAccessOrigin(raw) {
|
||||
const data = typeof raw === "string" ? JSON.parse(raw) : raw;
|
||||
if (!data || typeof data !== "object") {
|
||||
return {};
|
||||
}
|
||||
|
||||
const appIntel = splitHeaderPairs(data["Req-App-Intel"] || data["req-app-intel"]);
|
||||
const sysOrigin = splitHeaderPairs(data["Req-Sys-Origin"] || data["req-sys-origin"]);
|
||||
|
||||
return {
|
||||
authorization: data.Authorization || data.authorization,
|
||||
reqLang: data["Req-Lang"] || data["req-lang"],
|
||||
reqClient: data["Req-Client"] || data["req-client"],
|
||||
reqImei: data["Req-Imei"] || data["req-imei"] || appIntel["Req-Imei"],
|
||||
appVersion: appIntel.version,
|
||||
buildVersion: appIntel.build,
|
||||
appChannel: appIntel.channel,
|
||||
model: appIntel.model,
|
||||
sysVersion: appIntel.sysVersion,
|
||||
sysOrigin: sysOrigin.origin,
|
||||
sysOriginChild: sysOrigin.originChild || sysOrigin.child,
|
||||
};
|
||||
}
|
||||
|
||||
function setHeadersFromApp(info) {
|
||||
if (!info || typeof info !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
const next = {};
|
||||
const authorization = normalizeAuthorizationValue(info.authorization);
|
||||
if (authorization) {
|
||||
state.authorization = authorization;
|
||||
}
|
||||
if (info.reqLang) {
|
||||
next["Req-Lang"] = info.reqLang;
|
||||
}
|
||||
if (info.reqClient) {
|
||||
next["Req-Client"] = info.reqClient;
|
||||
}
|
||||
if (info.reqImei) {
|
||||
next["Req-Imei"] = info.reqImei;
|
||||
}
|
||||
if (info.sysOrigin) {
|
||||
next["Req-Sys-Origin"] =
|
||||
`origin=${info.sysOrigin};originChild=${info.sysOriginChild || info.sysOrigin}`;
|
||||
}
|
||||
if (info.appVersion || info.buildVersion || info.appChannel || info.model || info.sysVersion) {
|
||||
next["Req-App-Intel"] = buildAppIntelHeader({
|
||||
version: info.appVersion,
|
||||
build: info.buildVersion,
|
||||
channel: info.appChannel,
|
||||
model: info.model,
|
||||
sysVersion: info.sysVersion,
|
||||
imei: info.reqImei,
|
||||
});
|
||||
}
|
||||
|
||||
state.requestHeaders = { ...state.requestHeaders, ...next };
|
||||
}
|
||||
|
||||
function normalizeAuthorizationValue(value) {
|
||||
const normalized = trimValue(value);
|
||||
if (!normalized) {
|
||||
return "";
|
||||
}
|
||||
if (/^bearer\s+/i.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
return `Bearer ${normalized}`;
|
||||
}
|
||||
|
||||
function connectToApp() {
|
||||
return new Promise((resolve) => {
|
||||
let finished = false;
|
||||
const finish = (value) => {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
finished = true;
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
window.renderData = finish;
|
||||
window.getIosAccessOriginParam = finish;
|
||||
|
||||
if (!(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp)) {
|
||||
finish(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let attempt = 0;
|
||||
const poll = () => {
|
||||
attempt += 1;
|
||||
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
||||
try {
|
||||
finish(window.app.getAccessOrigin());
|
||||
} catch {
|
||||
finish(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (attempt < 50) {
|
||||
window.setTimeout(poll, 100);
|
||||
return;
|
||||
}
|
||||
if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") {
|
||||
window.FlutterApp.postMessage("requestAccessOrigin");
|
||||
}
|
||||
window.setTimeout(() => finish(null), 300);
|
||||
};
|
||||
|
||||
poll();
|
||||
}).then((value) => {
|
||||
if (value) {
|
||||
try {
|
||||
setHeadersFromApp(parseAccessOrigin(value));
|
||||
} catch {
|
||||
// App bridge data is best-effort; query token and default gateway headers still work.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value).replace(/[&<>"']/g, (match) => {
|
||||
const entityMap = {
|
||||
@ -544,11 +765,13 @@ function extractErrorMessage(payload) {
|
||||
async function requestJSON(path, options = {}) {
|
||||
const { method = "GET", headers = {}, body, searchParams } = options;
|
||||
const url = buildAPIURL(path, searchParams);
|
||||
const requestHeaders = shouldAttachGatewayHeaders(path) ? state.requestHeaders : {};
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method,
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...requestHeaders,
|
||||
...headers,
|
||||
},
|
||||
body,
|
||||
@ -565,6 +788,11 @@ async function requestJSON(path, options = {}) {
|
||||
return payload?.body || payload?.data || payload || null;
|
||||
}
|
||||
|
||||
function shouldAttachGatewayHeaders(path) {
|
||||
const normalizedPath = joinURLPath(path);
|
||||
return !normalizedPath.startsWith("/public/");
|
||||
}
|
||||
|
||||
function joinURLPath(...parts) {
|
||||
return `/${parts
|
||||
.map((part) => trimValue(part).replace(/^\/+|\/+$/g, ""))
|
||||
@ -945,6 +1173,7 @@ async function init() {
|
||||
bindEvents();
|
||||
setView(state.view);
|
||||
setupCountdown();
|
||||
await connectToApp();
|
||||
await refreshPageData();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user