Sync BIM login state and stabilize zoom
This commit is contained in:
parent
ce34ecdd4d
commit
249d46c7c7
|
|
@ -447,9 +447,10 @@ const configureCameraControl = () => {
|
|||
if (!control) return;
|
||||
activeZoomSpeed = normalizeZoomSpeed(activeZoomSpeed);
|
||||
configureCameraPanMap(control);
|
||||
control.followPointer = true;
|
||||
// Keep wheel dolly speed independent from whether the pointer is over model geometry.
|
||||
control.followPointer = false;
|
||||
control.doublePickFlyTo = false;
|
||||
control.smartPivot = true;
|
||||
control.smartPivot = false;
|
||||
control.zoomOnMouseWheel = true;
|
||||
control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate;
|
||||
control.mouseWheelDollyRate = activeZoomSpeed;
|
||||
|
|
@ -684,6 +685,8 @@ let nodeDCLogoutHandling = false;
|
|||
let nodeDCLoginHandling = false;
|
||||
let nodeDCSessionWatchTimer = null;
|
||||
let nodeDCSessionWatchInFlight = false;
|
||||
let nodeDCLoginSyncPrimed = false;
|
||||
let nodeDCLoginSyncEventId = "";
|
||||
|
||||
const handleNodeDCSessionLogout = async () => {
|
||||
if (nodeDCLogoutHandling) return;
|
||||
|
|
@ -844,9 +847,24 @@ const scheduleShareSessionPromotion = (shareToken) => {
|
|||
document.body.appendChild(iframe);
|
||||
};
|
||||
|
||||
const getLoginSyncEventId = (sessionPayload) => (
|
||||
typeof sessionPayload?.loginSyncEventId === "string" ? sessionPayload.loginSyncEventId : ""
|
||||
);
|
||||
|
||||
const applyLiveSessionPayload = async (sessionPayload, { reason = "watch" } = {}) => {
|
||||
const wasAuthenticated = runtimeSessionInfo.authenticated === true;
|
||||
const isAuthenticated = sessionPayload?.authenticated === true;
|
||||
const loginSyncEventId = getLoginSyncEventId(sessionPayload);
|
||||
const hasNewLoginSyncEvent =
|
||||
nodeDCLoginSyncPrimed &&
|
||||
!isAuthenticated &&
|
||||
!!loginSyncEventId &&
|
||||
loginSyncEventId !== nodeDCLoginSyncEventId;
|
||||
|
||||
nodeDCLoginSyncPrimed = true;
|
||||
if (loginSyncEventId) {
|
||||
nodeDCLoginSyncEventId = loginSyncEventId;
|
||||
}
|
||||
|
||||
runtimeSessionInfo = {
|
||||
...runtimeSessionInfo,
|
||||
|
|
@ -861,6 +879,10 @@ const applyLiveSessionPayload = async (sessionPayload, { reason = "watch" } = {}
|
|||
await handleNodeDCSessionLogout();
|
||||
return;
|
||||
}
|
||||
if (hasNewLoginSyncEvent) {
|
||||
handleNodeDCSessionLogin();
|
||||
return;
|
||||
}
|
||||
const shareToken = getShareTokenFromPath();
|
||||
if (shareToken && viewerMode === "guest") {
|
||||
scheduleShareSessionPromotion(shareToken);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ const BIM_SESSION_COOKIE = process.env.NODEDC_BIM_SESSION_COOKIE || "nodedc_bim_
|
|||
const BIM_HANDOFF_COOKIE = process.env.NODEDC_BIM_HANDOFF_COOKIE || "nodedc_bim_handoff";
|
||||
const BIM_SESSION_TTL_MS = Number(process.env.NODEDC_BIM_SESSION_TTL_MS || 12 * 60 * 60 * 1000);
|
||||
const BIM_HANDOFF_TTL_SECONDS = Number(process.env.NODEDC_BIM_HANDOFF_TTL_SECONDS || 60);
|
||||
const BIM_LOGIN_SYNC_TTL_MS = Number(process.env.NODEDC_BIM_LOGIN_SYNC_TTL_MS || 60 * 1000);
|
||||
const BIM_COOKIE_SECURE = parseBooleanEnv(process.env.NODEDC_BIM_COOKIE_SECURE, false);
|
||||
const BIM_COOKIE_SAMESITE = process.env.NODEDC_BIM_COOKIE_SAMESITE || (BIM_COOKIE_SECURE ? "None" : "Lax");
|
||||
const SHARE_AUTH_GUEST_PARAM = "ndc_bim_guest";
|
||||
|
|
@ -64,6 +65,7 @@ const CORS_ALLOWED_ORIGINS = String(process.env.NODEDC_BIM_ALLOWED_ORIGINS || ""
|
|||
.map((origin) => origin.trim())
|
||||
.filter(Boolean);
|
||||
const bimSessions = new Map();
|
||||
let latestBimLoginSyncEvent = null;
|
||||
|
||||
const MIME_TYPES = {
|
||||
".html": "text/html",
|
||||
|
|
@ -721,6 +723,27 @@ const parseJSONBody = async (req) => {
|
|||
|
||||
const randomBase64Url = (bytes = 24) => crypto.randomBytes(bytes).toString("base64url");
|
||||
|
||||
const markBimLoginSyncEvent = () => {
|
||||
const ttlMs = Number.isFinite(BIM_LOGIN_SYNC_TTL_MS) ? Math.max(1000, BIM_LOGIN_SYNC_TTL_MS) : 60 * 1000;
|
||||
latestBimLoginSyncEvent = {
|
||||
id: randomBase64Url(16),
|
||||
createdAt: nowIso(),
|
||||
expiresAt: Date.now() + ttlMs
|
||||
};
|
||||
return latestBimLoginSyncEvent;
|
||||
};
|
||||
|
||||
const getActiveBimLoginSyncEvent = () => {
|
||||
if (!latestBimLoginSyncEvent) {
|
||||
return null;
|
||||
}
|
||||
if (latestBimLoginSyncEvent.expiresAt <= Date.now()) {
|
||||
latestBimLoginSyncEvent = null;
|
||||
return null;
|
||||
}
|
||||
return latestBimLoginSyncEvent;
|
||||
};
|
||||
|
||||
const sanitizeReturnTo = (value, fallback = "/") => {
|
||||
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//")) {
|
||||
return fallback;
|
||||
|
|
@ -1749,16 +1772,30 @@ const handleGetUploadVersions = async (req, res, searchParams) => {
|
|||
|
||||
const handleAuthSession = async (req, res) => {
|
||||
const session = await getValidatedBimSession(req, res);
|
||||
const loginSyncEvent = getActiveBimLoginSyncEvent();
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
sendJSON(res, 200, {
|
||||
ok: true,
|
||||
authRequired: BIM_AUTH_REQUIRED,
|
||||
authenticated: !!session,
|
||||
canShare: !BIM_AUTH_REQUIRED || !!session,
|
||||
user: session?.user || null,
|
||||
loginSyncEventId: loginSyncEvent?.id || null,
|
||||
loginSyncEventCreatedAt: loginSyncEvent?.createdAt || null,
|
||||
loginUrl: session ? null : buildLauncherLoginUrl(req, "/")
|
||||
});
|
||||
};
|
||||
|
||||
const handleLoginSync = (res) => {
|
||||
const event = markBimLoginSyncEvent();
|
||||
setCors(res);
|
||||
res.statusCode = 200;
|
||||
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
res.setHeader("X-NODEDC-Session-Event", event.id);
|
||||
res.end("ok");
|
||||
};
|
||||
|
||||
const handleLauncherHandoff = async (req, res, url) => {
|
||||
const token = url.searchParams.get("token") || "";
|
||||
const nextPath = sanitizeReturnTo(url.searchParams.get("next_path") || url.searchParams.get("returnTo") || "/");
|
||||
|
|
@ -3435,6 +3472,10 @@ const requestHandler = async (req, res) => {
|
|||
return handleSharePromoted(res, url);
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/auth/login-sync") {
|
||||
return handleLoginSync(res);
|
||||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/auth/logout") {
|
||||
return handleLogout(req, res);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue