修复中控

This commit is contained in:
dekun
2026-05-30 15:19:38 +08:00
parent 4cd5a48dc1
commit a67d7aa58b
7 changed files with 447 additions and 53 deletions
+269 -5
View File
@@ -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 %}