This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new 10b3ee8 fix: slide session cookie maxAge + clear 3D selection on
empty-plane click (#29)
10b3ee8 is described below
commit 10b3ee83fac9d2f586b48b5c9219ce98df018339
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Thu May 28 21:14:11 2026 +0800
fix: slide session cookie maxAge + clear 3D selection on empty-plane click
(#29)
* fix(auth): slide the session cookie maxAge on each request
The server slides a session's TTL on every authenticated request
(`sessions.touch()`), but the cookie's maxAge was stamped only at
login. So an actively-used session kept sliding server-side while the
browser cookie still expired at login + ttl — the user was silently
logged out mid-session. requireAuth now re-stamps the cookie (same
options as login, only maxAge refreshed) after a successful touch, so
the browser expiry tracks the sliding server TTL.
* fix(infra-3d): clear selection when clicking empty plane space
pmndrs `pointermissed` only fires on a true void hit, so clicking a
colored zone/tier plane (raycast left on so planes occlude cubes) never
cleared the selection — only clicking past every plane did. A deselect
handler on the planes isn't an option: operators orbit-drag from empty
plane space to rotate WITHOUT changing selection.
Detect the empty click at the DOM level on the canvas, guarded so it
only fires for a genuine empty click:
- target is the <canvas> (not a portaled <Html> overlay — clicking
the detail card must not deselect)
- pointer barely moved (a click, not an orbit/pan drag)
- no cube absorbed the gesture (onNodeClick stamped within 120ms)
Replaces the @pointermissed wiring. Needs a live click-test before merge.
---
apps/bff/src/user/middleware.ts | 14 +++++++++
apps/ui/src/features/infra-3d/Infra3DScene.vue | 41 +++++++++++++++++++++-----
2 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/apps/bff/src/user/middleware.ts b/apps/bff/src/user/middleware.ts
index bc7a589..9a7d616 100644
--- a/apps/bff/src/user/middleware.ts
+++ b/apps/bff/src/user/middleware.ts
@@ -60,6 +60,20 @@ export function requireAuth(deps: AuthDeps) {
return void reply.code(401).send({ error: 'unauthenticated' });
}
req.session = session;
+ // Sliding session: touch() just slid the server-side TTL, so
+ // re-stamp the cookie's maxAge to match. The cookie's expiry was
+ // set only at login, so without this an actively-used session still
+ // expires in the browser at login + ttl while the server believes
+ // it's alive — the user is logged out mid-session. Mirror the login
+ // cookie options exactly so only maxAge slides.
+ const s = deps.config.current.session;
+ reply.setCookie(s.cookieName, sid, {
+ httpOnly: true,
+ sameSite: 'strict',
+ secure: s.cookieSecure,
+ path: '/',
+ maxAge: s.ttlMinutes * 60,
+ });
};
}
diff --git a/apps/ui/src/features/infra-3d/Infra3DScene.vue
b/apps/ui/src/features/infra-3d/Infra3DScene.vue
index 14b6a4b..eb5a8af 100644
--- a/apps/ui/src/features/infra-3d/Infra3DScene.vue
+++ b/apps/ui/src/features/infra-3d/Infra3DScene.vue
@@ -808,6 +808,9 @@ function onNodePointerOut(node: SceneServiceNode): void {
* safe).
*/
function onNodeClick(node: SceneServiceNode): void {
+ // Stamp so the canvas-host pointerup handler knows a cube absorbed
+ // this gesture and must not treat the same release as an empty click.
+ lastCubeClickAt = performance.now();
dbgBadge(`CUBE CLICKED → ${node.shortName}`, '#f97316',
`(${node.layerKey})`);
dbg('node.pointerdown', {
shortName: node.shortName,
@@ -853,12 +856,30 @@ function nodeHandlers(node: SceneServiceNode):
NodeHandlers {
return h;
}
-/** Fired by TresCanvas when a pointer event hits nothing in the
- * scene — the pmndrs "pointer-missed" pattern. Empty-space click =
- * clear the current selection. */
-function onPointerMissed(): void {
- dbg('canvas.pointermissed', { selected: props.selectedNodeId });
- emit('select', null);
+// Empty-space deselect. pmndrs `pointermissed` only fires on a true
+// void hit (the ray hits nothing) — clicking a colored zone/tier plane
+// hits the plane (raycast stays on so planes occlude cubes), so it
+// never cleared the selection. We can't add a deselect handler to the
+// planes either: operators orbit-drag FROM empty plane space to rotate
+// WITHOUT changing selection (see CLAUDE.md). So detect an empty click
+// at the DOM level on the canvas itself, with three guards:
+// - target is the <canvas> (not a portaled <Html> overlay like the
+// detail card — clicking the card must not deselect)
+// - the pointer barely moved (a click, not an orbit/pan drag)
+// - no cube absorbed this gesture (onNodeClick stamped recently)
+let lastCubeClickAt = 0;
+let pointerDownX = 0;
+let pointerDownY = 0;
+const CLICK_SLOP_PX = 5;
+function onHostPointerDown(e: PointerEvent): void {
+ pointerDownX = e.clientX;
+ pointerDownY = e.clientY;
+}
+function onHostPointerUp(e: PointerEvent): void {
+ if ((e.target as HTMLElement | null)?.tagName !== 'CANVAS') return;
+ if (Math.hypot(e.clientX - pointerDownX, e.clientY - pointerDownY) >
CLICK_SLOP_PX) return;
+ if (performance.now() - lastCubeClickAt < 120) return;
+ if (props.selectedNodeId !== null) emit('select', null);
}
// Diagnostic — fires whenever the parent updates `selectedNodeId`.
@@ -1116,13 +1137,17 @@ onUnmounted(() => {
</script>
<template>
- <div ref="canvasHostEl" class="canvas-host">
+ <div
+ ref="canvasHostEl"
+ class="canvas-host"
+ @pointerdown="onHostPointerDown"
+ @pointerup="onHostPointerUp"
+ >
<TresCanvas
clear-color="#0a0d12"
:antialias="true"
power-preference="high-performance"
@loop="onSceneLoop"
- @pointermissed="onPointerMissed"
>
<TresPerspectiveCamera
ref="cameraRef"