Sync BIM session logout with Hub

This commit is contained in:
CODEX 2026-06-23 14:48:37 +03:00
parent 48400c7d8c
commit 66fc4960bf
4 changed files with 132 additions and 11 deletions

View File

@ -3220,20 +3220,28 @@ header {
} }
body[data-viewer-mode="guest"] .tb-btn:not([data-action="measure"]):not([data-action="section"]):not([data-action="projection"]), body[data-viewer-mode="guest"] .tb-btn:not([data-action="measure"]):not([data-action="section"]):not([data-action="projection"]),
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) .tb-btn:not([data-action="measure"]):not([data-action="section"]):not([data-action="projection"]), html[data-share-startup] body:not([data-viewer-mode="standard"]) .tb-btn:not([data-action="measure"]):not([data-action="section"]):not([data-action="projection"]),
body[data-viewer-mode="guest"] #settingsPanel, body[data-viewer-mode="guest"] #settingsPanel,
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) #settingsPanel, html[data-share-startup] body:not([data-viewer-mode="standard"]) #settingsPanel,
body[data-viewer-mode="guest"] #templatesPanel, body[data-viewer-mode="guest"] #templatesPanel,
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) #templatesPanel, html[data-share-startup] body:not([data-viewer-mode="standard"]) #templatesPanel,
body[data-viewer-mode="guest"] #projectNameSection, body[data-viewer-mode="guest"] #projectNameSection,
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) #projectNameSection, html[data-share-startup] body:not([data-viewer-mode="standard"]) #projectNameSection,
body[data-viewer-mode="guest"] #saveProjectSection, body[data-viewer-mode="guest"] #saveProjectSection,
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) #saveProjectSection, html[data-share-startup] body:not([data-viewer-mode="standard"]) #saveProjectSection,
body[data-viewer-mode="guest"] #modelVersionUploadButton, body[data-viewer-mode="guest"] #modelVersionUploadButton,
html[data-share-startup="guest"] body:not([data-viewer-mode="standard"]) #modelVersionUploadButton { html[data-share-startup] body:not([data-viewer-mode="standard"]) #modelVersionUploadButton {
display: none !important; display: none !important;
} }
html[data-share-startup="pending"] #dropZone {
display: none !important;
}
html[data-share-startup="pending"] #loader.hidden {
display: block;
}
.version-history-backdrop { .version-history-backdrop {
position: fixed; position: fixed;
inset: 0; inset: 0;

View File

@ -186,6 +186,7 @@ const COMMENTS_API = "/api/comments";
const UPLOAD_VERSIONS_API = "/api/uploads/versions"; const UPLOAD_VERSIONS_API = "/api/uploads/versions";
const SHARE_AUTH_GUEST_PARAM = "ndc_bim_guest"; const SHARE_AUTH_GUEST_PARAM = "ndc_bim_guest";
const SHARE_PROMOTION_TIMEOUT_MS = 4500; const SHARE_PROMOTION_TIMEOUT_MS = 4500;
const NODEDC_SESSION_EVENT_TYPE = "nodedc:session:logout";
const acceptByType = { const acceptByType = {
xkt: ".xkt", xkt: ".xkt",
@ -648,6 +649,101 @@ const clearShareGuestAuthMarker = () => {
} }
}; };
const clearShareStartupPending = () => {
if (document.documentElement?.dataset?.shareStartup === "pending") {
delete document.documentElement.dataset.shareStartup;
}
};
const isNodeDCSessionLogoutEvent = (value) => (
value &&
typeof value === "object" &&
value.type === NODEDC_SESSION_EVENT_TYPE &&
typeof value.id === "string"
);
let nodeDCSessionSyncCleanup = null;
let nodeDCLogoutHandling = false;
const handleNodeDCSessionLogout = async () => {
if (nodeDCLogoutHandling) return;
nodeDCLogoutHandling = true;
const logoutRequest = fetch("/logout", {
method: "GET",
credentials: "include",
keepalive: true,
cache: "no-store",
})
.catch(() => undefined);
runtimeSessionInfo = {
...runtimeSessionInfo,
authenticated: false,
canShare: false,
user: null,
};
if (startupSharePayload) {
startupSharePayload = {
...startupSharePayload,
mode: "guest",
readonly: true,
authenticated: false,
canShare: false,
user: null,
};
}
if (getShareTokenFromPath()) {
viewerMode = "guest";
inspectorCollapsed = true;
applyRuntimeMode();
updatePanelsVisibility();
nodeDCLogoutHandling = false;
return;
}
await Promise.race([
logoutRequest,
new Promise((resolve) => window.setTimeout(resolve, 500)),
]);
const loginUrl = new URL("/auth/login", getLauncherUrl());
window.location.replace(loginUrl.toString());
};
const setupNodeDCSessionSync = () => {
if (nodeDCSessionSyncCleanup || typeof document === "undefined") return;
let launcherOrigin = "";
try {
launcherOrigin = new URL(getLauncherUrl()).origin;
} catch (_) {
return;
}
const iframe = document.createElement("iframe");
iframe.src = new URL("/auth/session-sync", launcherOrigin).toString();
iframe.title = "NODE.DC session sync";
iframe.tabIndex = -1;
iframe.setAttribute("aria-hidden", "true");
iframe.style.cssText = "position:fixed;width:0;height:0;opacity:0;pointer-events:none;border:0;";
document.body.appendChild(iframe);
const handleMessage = (event) => {
if (event.origin !== launcherOrigin || !isNodeDCSessionLogoutEvent(event.data)) return;
void handleNodeDCSessionLogout();
};
window.addEventListener("message", handleMessage);
nodeDCSessionSyncCleanup = () => {
window.removeEventListener("message", handleMessage);
iframe.remove();
nodeDCSessionSyncCleanup = null;
};
};
const scheduleShareSessionPromotion = (shareToken) => { const scheduleShareSessionPromotion = (shareToken) => {
const silentPromoteUrl = startupSharePayload?.silentPromoteUrl; const silentPromoteUrl = startupSharePayload?.silentPromoteUrl;
if ( if (
@ -5212,6 +5308,7 @@ const applyViewerState = (state) => {
const initViewer = async () => { const initViewer = async () => {
await initializeRuntimeMode(); await initializeRuntimeMode();
setupNodeDCSessionSync();
const preloadHidden = document.getElementById("preload-hidden"); const preloadHidden = document.getElementById("preload-hidden");
if (preloadHidden) { if (preloadHidden) {
@ -5842,6 +5939,8 @@ const initViewer = async () => {
}); });
if (!src) { if (!src) {
reportViewerEvent("startup-no-src", { search: window.location.search }, "error"); reportViewerEvent("startup-no-src", { search: window.location.search }, "error");
clearShareStartupPending();
setLoading(false);
return; return;
} }
@ -5850,12 +5949,15 @@ const initViewer = async () => {
if (!type) { if (!type) {
reportViewerEvent("startup-type-error", { src, name, typeParam: params.get("type") }, "error"); reportViewerEvent("startup-type-error", { src, name, typeParam: params.get("type") }, "error");
showError("Не удалось определить формат модели из URL"); showError("Не удалось определить формат модели из URL");
clearShareStartupPending();
setLoading(false);
return; return;
} }
dropZone?.classList.add("hidden"); dropZone?.classList.add("hidden");
setStatus(`Подготовка: ${name}...`); setStatus(`Подготовка: ${name}...`);
setLoading(true); setLoading(true);
clearShareStartupPending();
let settingsSrc = sharedViewer?.settingsSrc || params.get("settingsSrc") || params.get("sourceSrc") || params.get("originalSrc") || src; let settingsSrc = sharedViewer?.settingsSrc || params.get("settingsSrc") || params.get("sourceSrc") || params.get("originalSrc") || src;
const applyQueryViewerOverrides = () => { const applyQueryViewerOverrides = () => {

View File

@ -16,7 +16,7 @@
<script> <script>
(function () { (function () {
if (/^\/share\/[^/]+\/?$/.test(window.location.pathname || "")) { if (/^\/share\/[^/]+\/?$/.test(window.location.pathname || "")) {
document.documentElement.dataset.shareStartup = "guest"; document.documentElement.dataset.shareStartup = "pending";
} }
}()); }());
</script> </script>

View File

@ -1685,15 +1685,22 @@ const handleSharePromoted = (res, url) => {
</html>`); </html>`);
}; };
const handleLogout = (req, res) => { const handleLogout = (req, res, {redirect = true} = {}) => {
const sessionId = parseCookies(req.headers.cookie || "")[BIM_SESSION_COOKIE]; const sessionId = parseCookies(req.headers.cookie || "")[BIM_SESSION_COOKIE];
if (sessionId) { if (sessionId) {
bimSessions.delete(sessionId); bimSessions.delete(sessionId);
} }
clearBimSessionCookie(res); clearBimSessionCookie(res);
res.setHeader("Cache-Control", "no-store");
if (redirect) {
res.statusCode = 302; res.statusCode = 302;
res.setHeader("Location", "/"); res.setHeader("Location", "/");
res.end(); res.end();
return;
}
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.end("<!doctype html><html><head><meta charset='utf-8'></head><body>NODE.DC BIM session closed.</body></html>");
}; };
const handleCreateShare = async (req, res) => { const handleCreateShare = async (req, res) => {
@ -3304,6 +3311,10 @@ const requestHandler = async (req, res) => {
return handleLogout(req, res); return handleLogout(req, res);
} }
if (req.method === "GET" && url.pathname === "/logout") {
return handleLogout(req, res, {redirect: false});
}
if (req.method === "GET" && /^\/api\/shares\/[^/]+\/?$/.test(url.pathname)) { if (req.method === "GET" && /^\/api\/shares\/[^/]+\/?$/.test(url.pathname)) {
return handleGetShare(req, res, shareTokenFromPath(url.pathname)); return handleGetShare(req, res, shareTokenFromPath(url.pathname));
} }