修复中控
This commit is contained in:
+269
-5
@@ -106,6 +106,15 @@
|
||||
<button type="button" class="btn-frame-back" id="frame-back-overview" title="返回服务总览">
|
||||
总览
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-frame-back"
|
||||
id="frame-back-hub"
|
||||
title="返回复盘中控监控区"
|
||||
hidden
|
||||
>
|
||||
← 返回中控
|
||||
</button>
|
||||
<span class="frame-title" id="current-service-name"></span>
|
||||
</div>
|
||||
<div class="frame-toolbar-actions">
|
||||
@@ -118,6 +127,15 @@
|
||||
>
|
||||
中控登录
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-toolbar-refresh"
|
||||
id="frame-instance-sso"
|
||||
title="通过本地导航代签实例 SSO(需 NAV_HUB_USERNAME / NAV_HUB_PASSWORD)"
|
||||
hidden
|
||||
>
|
||||
实例免密
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-toolbar-refresh" id="frame-refresh">
|
||||
刷新
|
||||
</button>
|
||||
@@ -132,7 +150,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="frame-wrap">
|
||||
<iframe id="svc-frame" title="内嵌服务" hidden></iframe>
|
||||
<iframe id="svc-frame" name="svc-frame" title="内嵌服务" hidden></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,18 +199,55 @@
|
||||
var btnRefresh = document.getElementById("frame-refresh");
|
||||
var btnForceRefresh = document.getElementById("frame-force-refresh");
|
||||
var btnBack = document.getElementById("frame-back-overview");
|
||||
var btnBackHub = document.getElementById("frame-back-hub");
|
||||
var btnHubLogin = document.getElementById("frame-hub-login");
|
||||
var btnInstanceSso = document.getElementById("frame-instance-sso");
|
||||
var currentBaseUrl = "";
|
||||
var currentOpenUrl = "";
|
||||
var currentEmbedKind = "";
|
||||
var currentServiceId = "";
|
||||
var currentOrigin = "";
|
||||
var currentNextPath = "/monitor";
|
||||
var currentViewMode = "service";
|
||||
var hubReturnState = null;
|
||||
var instanceNavCtx = null;
|
||||
|
||||
function normalizeOrigin(raw) {
|
||||
if (!raw) return "";
|
||||
try {
|
||||
var u = new URL(raw.indexOf("://") >= 0 ? raw : "http://" + raw);
|
||||
var port = u.port;
|
||||
if (u.protocol === "https:" && (!port || port === "443")) {
|
||||
return u.protocol + "//" + u.hostname;
|
||||
}
|
||||
if (u.protocol === "http:" && (!port || port === "80")) {
|
||||
return u.protocol + "//" + u.hostname;
|
||||
}
|
||||
return u.origin;
|
||||
} catch (e) {
|
||||
return String(raw).replace(/\/+$/, "");
|
||||
}
|
||||
}
|
||||
|
||||
function originsCompatible(expected, actual) {
|
||||
var e = normalizeOrigin(expected);
|
||||
var a = normalizeOrigin(actual);
|
||||
if (!e || !a) return true;
|
||||
return e === a;
|
||||
}
|
||||
|
||||
function isHubEmbed(kind) {
|
||||
return (kind || "").toLowerCase() === "hub";
|
||||
}
|
||||
|
||||
function toggleInstanceBackBtn(show) {
|
||||
if (btnBackHub) btnBackHub.hidden = !show;
|
||||
}
|
||||
|
||||
function toggleInstanceSsoBtn(show) {
|
||||
if (btnInstanceSso) btnInstanceSso.hidden = !show;
|
||||
}
|
||||
|
||||
function toggleHubLoginBtn(show) {
|
||||
if (btnHubLogin) btnHubLogin.hidden = !show;
|
||||
}
|
||||
@@ -202,6 +257,48 @@
|
||||
frame.src = url;
|
||||
}
|
||||
|
||||
function iframeLooksLikeHub(href) {
|
||||
if (!href) return false;
|
||||
var hubOrigin = normalizeOrigin(currentOrigin);
|
||||
var h = String(href);
|
||||
if (hubOrigin && h.indexOf(hubOrigin) !== 0) return false;
|
||||
return (
|
||||
/\/monitor(\?|#|$)/.test(h) ||
|
||||
h.indexOf("/embed-auth") >= 0 ||
|
||||
(h.indexOf("/login") >= 0 && h.indexOf("embed=1") >= 0) ||
|
||||
h.indexOf("/settings") >= 0
|
||||
);
|
||||
}
|
||||
|
||||
function syncHubInstanceBackBtn() {
|
||||
if (!isHubEmbed(currentEmbedKind) || frameStack.hidden || !currentBaseUrl) {
|
||||
toggleInstanceBackBtn(false);
|
||||
toggleInstanceSsoBtn(false);
|
||||
return;
|
||||
}
|
||||
var onHub = false;
|
||||
try {
|
||||
onHub = iframeLooksLikeHub(frame.contentWindow.location.href);
|
||||
} catch (e) {
|
||||
onHub = false;
|
||||
}
|
||||
if (onHub) {
|
||||
currentViewMode = "hub";
|
||||
toggleInstanceBackBtn(false);
|
||||
toggleInstanceSsoBtn(false);
|
||||
toggleHubLoginBtn(true);
|
||||
return;
|
||||
}
|
||||
currentViewMode = "hub-instance";
|
||||
toggleInstanceBackBtn(true);
|
||||
toggleHubLoginBtn(false);
|
||||
toggleInstanceSsoBtn(!!(instanceNavCtx && instanceNavCtx.exchangeId));
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
frame.addEventListener("load", syncHubInstanceBackBtn);
|
||||
}
|
||||
|
||||
function hubLoginViaProxy(done) {
|
||||
if (!currentServiceId && !currentOrigin) {
|
||||
if (done) done(false, "未选择中控服务");
|
||||
@@ -234,12 +331,127 @@
|
||||
|
||||
window.addEventListener("message", function (ev) {
|
||||
var data = ev.data;
|
||||
if (!data || data.type !== "hub:login-ok") return;
|
||||
if (data.embed_auth_url) {
|
||||
applyIframeUrl(data.embed_auth_url);
|
||||
if (!data || !data.type) return;
|
||||
if (data.type === "hub:login-ok") {
|
||||
if (data.embed_auth_url) {
|
||||
applyIframeUrl(data.embed_auth_url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (data.type === "hub:open-instance-nav") {
|
||||
instanceNavCtx = {
|
||||
exchangeId: String(data.exchangeId || ""),
|
||||
nextPath: data.nextPath || "/",
|
||||
title: data.title || "交易所实例",
|
||||
serviceId: currentServiceId,
|
||||
};
|
||||
currentViewMode = "hub-instance";
|
||||
if (data.title) nameEl.textContent = data.title;
|
||||
toggleHubLoginBtn(false);
|
||||
toggleInstanceBackBtn(true);
|
||||
toggleInstanceSsoBtn(!!instanceNavCtx.exchangeId);
|
||||
return;
|
||||
}
|
||||
if (data.type !== "hub:open-instance") return;
|
||||
if (frameStack.hidden || !currentServiceId) return;
|
||||
if (!originsCompatible(currentOrigin, ev.origin)) {
|
||||
console.warn(
|
||||
"[LocalNav] hub:open-instance origin 不匹配,已忽略",
|
||||
normalizeOrigin(currentOrigin),
|
||||
normalizeOrigin(ev.origin)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!data.url) return;
|
||||
|
||||
try {
|
||||
if (ev.source) {
|
||||
ev.source.postMessage({ type: "hub:open-instance-ack", ok: true }, ev.origin || "*");
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
hubReturnState = {
|
||||
openUrl: currentOpenUrl,
|
||||
baseUrl: currentBaseUrl,
|
||||
name: nameEl.textContent,
|
||||
embedKind: currentEmbedKind,
|
||||
serviceId: currentServiceId,
|
||||
origin: currentOrigin,
|
||||
nextPath: currentNextPath,
|
||||
};
|
||||
instanceNavCtx = {
|
||||
exchangeId: String(data.exchangeId || ""),
|
||||
nextPath: data.nextPath || "/",
|
||||
title: data.title || "交易所实例",
|
||||
serviceId: currentServiceId,
|
||||
};
|
||||
currentViewMode = "hub-instance";
|
||||
nameEl.textContent = instanceNavCtx.title;
|
||||
toggleHubLoginBtn(false);
|
||||
toggleInstanceBackBtn(true);
|
||||
applyIframeUrl(data.url);
|
||||
});
|
||||
|
||||
function refreshInstanceViaProxy(done) {
|
||||
if (!instanceNavCtx || !instanceNavCtx.exchangeId) {
|
||||
if (done) done(false, null, "缺少实例上下文");
|
||||
return;
|
||||
}
|
||||
fetch("/api/embed/hub-instance-url", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
||||
body: JSON.stringify({
|
||||
service_id: parseInt(instanceNavCtx.serviceId, 10) || undefined,
|
||||
exchange_id: instanceNavCtx.exchangeId,
|
||||
next: instanceNavCtx.nextPath || "/",
|
||||
embed: "1",
|
||||
}),
|
||||
})
|
||||
.then(function (r) {
|
||||
return r.json().then(function (j) {
|
||||
return { ok: r.ok, j: j };
|
||||
});
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.ok && res.j.ok && res.j.url) {
|
||||
if (done) done(true, res.j.url, null);
|
||||
return;
|
||||
}
|
||||
if (done) done(false, null, (res.j && res.j.detail) || "无法重新打开实例");
|
||||
})
|
||||
.catch(function (e) {
|
||||
if (done) done(false, null, String(e));
|
||||
});
|
||||
}
|
||||
|
||||
function returnToHubMonitor() {
|
||||
var st = hubReturnState;
|
||||
currentViewMode = "hub";
|
||||
instanceNavCtx = null;
|
||||
hubReturnState = null;
|
||||
toggleInstanceBackBtn(false);
|
||||
toggleInstanceSsoBtn(false);
|
||||
if (st) {
|
||||
currentOpenUrl = st.openUrl || currentOpenUrl;
|
||||
currentBaseUrl = st.baseUrl || st.openUrl || currentBaseUrl;
|
||||
currentEmbedKind = st.embedKind || currentEmbedKind;
|
||||
currentServiceId = st.serviceId || currentServiceId;
|
||||
currentOrigin = st.origin || currentOrigin;
|
||||
currentNextPath = st.nextPath || currentNextPath;
|
||||
nameEl.textContent = st.name || nameEl.textContent;
|
||||
}
|
||||
toggleHubLoginBtn(isHubEmbed(currentEmbedKind));
|
||||
if (isHubEmbed(currentEmbedKind) && hubAutoLogin) {
|
||||
hubLoginViaProxy(function (ok) {
|
||||
if (!ok) applyIframeUrl(currentOpenUrl || currentBaseUrl);
|
||||
syncHubInstanceBackBtn();
|
||||
});
|
||||
return;
|
||||
}
|
||||
applyIframeUrl(currentOpenUrl || currentBaseUrl);
|
||||
syncHubInstanceBackBtn();
|
||||
}
|
||||
|
||||
function setActive(el) {
|
||||
links.forEach(function (a) {
|
||||
a.classList.remove("active");
|
||||
@@ -264,6 +476,10 @@
|
||||
currentServiceId = meta.serviceId || "";
|
||||
currentOrigin = meta.origin || "";
|
||||
currentNextPath = meta.nextPath || "/monitor";
|
||||
currentViewMode = isHubEmbed(currentEmbedKind) ? "hub" : "service";
|
||||
instanceNavCtx = null;
|
||||
hubReturnState = null;
|
||||
toggleInstanceBackBtn(false);
|
||||
nameEl.textContent = name || "";
|
||||
dashboard.hidden = true;
|
||||
frameStack.hidden = false;
|
||||
@@ -307,12 +523,33 @@
|
||||
}
|
||||
|
||||
function reloadUrl() {
|
||||
if (currentViewMode === "hub-instance") {
|
||||
refreshInstanceViaProxy(function (ok, url, err) {
|
||||
if (ok && url) applyIframeUrl(url);
|
||||
else if (err) window.alert(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var u = currentOpenUrl || currentBaseUrl;
|
||||
if (!u) return;
|
||||
frame.src = buildCacheBustUrl(u, false);
|
||||
}
|
||||
|
||||
function forceReloadUrl() {
|
||||
if (currentViewMode === "hub-instance") {
|
||||
refreshInstanceViaProxy(function (ok, url, err) {
|
||||
if (!ok || !url) {
|
||||
if (err) window.alert(err);
|
||||
return;
|
||||
}
|
||||
frame.src = "about:blank";
|
||||
frame.onload = function () {
|
||||
frame.onload = null;
|
||||
frame.src = buildCacheBustUrl(url, true);
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
var u = currentOpenUrl || currentBaseUrl;
|
||||
if (!u) return;
|
||||
frame.src = "about:blank";
|
||||
@@ -328,11 +565,15 @@
|
||||
currentEmbedKind = "";
|
||||
currentServiceId = "";
|
||||
currentOrigin = "";
|
||||
currentViewMode = "service";
|
||||
instanceNavCtx = null;
|
||||
hubReturnState = null;
|
||||
frame.src = "about:blank";
|
||||
frame.hidden = true;
|
||||
frameStack.hidden = true;
|
||||
dashboard.hidden = false;
|
||||
toggleHubLoginBtn(false);
|
||||
toggleInstanceSsoBtn(false);
|
||||
setActive(null);
|
||||
}
|
||||
|
||||
@@ -368,7 +609,24 @@
|
||||
btnHubLogin.disabled = true;
|
||||
hubLoginViaProxy(function (ok, err) {
|
||||
btnHubLogin.disabled = false;
|
||||
if (!ok && err) window.alert(err);
|
||||
if (!ok && err) {
|
||||
window.alert("中控登录失败:\n" + err + "\n\n请检查 LocalNav .env 的 NAV_HUB_USERNAME / NAV_HUB_PASSWORD 是否与云端 hub .env 一致。");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (btnInstanceSso) {
|
||||
btnInstanceSso.addEventListener("click", function () {
|
||||
btnInstanceSso.disabled = true;
|
||||
refreshInstanceViaProxy(function (ok, url, err) {
|
||||
btnInstanceSso.disabled = false;
|
||||
if (ok && url) {
|
||||
applyIframeUrl(url);
|
||||
syncHubInstanceBackBtn();
|
||||
return;
|
||||
}
|
||||
if (err) window.alert("实例免密失败:\n" + err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -384,6 +642,12 @@
|
||||
btnBack.addEventListener("click", function () {
|
||||
showDashboard();
|
||||
});
|
||||
|
||||
if (btnBackHub) {
|
||||
btnBackHub.addEventListener("click", function () {
|
||||
returnToHubMonitor();
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user