This is an automated email from the ASF dual-hosted git repository.

LauraXia123 pushed a commit to branch issue-12588
in repository https://gitbox.apache.org/repos/asf/gravitino.git

commit 6a9b1bb89088518243711a483e4a29bfe9731449
Author: Qian Xia <[email protected]>
AuthorDate: Tue Aug 25 14:18:27 2026 +0800

    [#12588] improvement(web2-ui): Expose UI session timeout settings through 
server configuration
---
 conf/gravitino.conf.template                       | 14 ++++-
 .../web/src/lib/hooks/useAbsoluteSessionTimeout.js | 10 +---
 web-v2/web/src/lib/hooks/useIdleTimeout.js         |  8 +--
 web-v2/web/src/lib/provider/IdleSessionProvider.js | 51 +++++++++---------
 .../web/src/lib/provider/sessionTimeoutConfig.js   | 62 ++++++++++++++++++++++
 5 files changed, 103 insertions(+), 42 deletions(-)

diff --git a/conf/gravitino.conf.template b/conf/gravitino.conf.template
index c9afcc025c..861dd8829d 100644
--- a/conf/gravitino.conf.template
+++ b/conf/gravitino.conf.template
@@ -43,6 +43,17 @@ gravitino.server.webserver.threadPoolWorkQueueSize = 100
 gravitino.server.webserver.requestHeaderSize = 131072
 # The response header size of the built-in web server
 gravitino.server.webserver.responseHeaderSize = 131072
+# UI inactivity timeout in milliseconds. The UI environment variable
+# NEXT_PUBLIC_IDLE_TIMEOUT_MS is used when this config is not set.
+# gravitino.server.webserver.sessionIdleTimeoutMs = 900000
+# Maximum UI session duration in milliseconds, regardless of user activity. 
The UI environment
+# variable NEXT_PUBLIC_MAX_SESSION_DURATION_MS is used when this config is not 
set.
+# gravitino.server.webserver.sessionMaxDurationMs = 18000000
+# UI warning countdown duration in milliseconds before the inactivity timeout. 
The UI environment
+# variable NEXT_PUBLIC_IDLE_WARNING_LEAD_MS is used when this config is not 
set.
+# gravitino.server.webserver.sessionIdleWarningLeadMs = 60000
+# multiple visibleConfigs are spitted by comma.
+# 
gravitino.server.visibleConfigs=gravitino.server.webserver.sessionIdleTimeoutMs,gravitino.server.webserver.sessionMaxDurationMs,gravitino.server.webserver.sessionIdleWarningLeadMs
 
 # THE CONFIGURATION FOR Gravitino ENTITY STORE
 # The entity store to use, we only supports relational
@@ -88,8 +99,7 @@ gravitino.fetchFile.blockUnsafeRemoteUri = true
 # THE CONFIGURATION FOR authorization
 # Whether Gravitino enable authorization or not
 gravitino.authorization.enable = false
-# The admins of Gravitino service, multiple admins are spitted by comma.
-gravitino.server.visibleConfigs=gravitino.authorization.serviceAdmins
+# The admins of Gravitino service
 gravitino.authorization.serviceAdmins = anonymous
 
 # THE CONFIGURATION FOR AUXILIARY SERVICE
diff --git a/web-v2/web/src/lib/hooks/useAbsoluteSessionTimeout.js 
b/web-v2/web/src/lib/hooks/useAbsoluteSessionTimeout.js
index 58dab82730..41a6d84946 100644
--- a/web-v2/web/src/lib/hooks/useAbsoluteSessionTimeout.js
+++ b/web-v2/web/src/lib/hooks/useAbsoluteSessionTimeout.js
@@ -30,14 +30,8 @@ const SESSION_START_KEY = 'grtv-session-start'
 
 /**
  * Default maximum session duration: 5 hours in milliseconds.
- * Overridable via NEXT_PUBLIC_MAX_SESSION_DURATION_MS environment variable.
  */
-const DEFAULT_MAX_SESSION_DURATION_MS = (() => {
-  const envVal = process.env.NEXT_PUBLIC_MAX_SESSION_DURATION_MS
-  const parsed = envVal ? Number(envVal) : NaN
-
-  return Number.isFinite(parsed) && parsed > 0 ? parsed : 5 * 60 * 60 * 1000
-})()
+const DEFAULT_MAX_SESSION_DURATION_MS = 5 * 60 * 60 * 1000
 
 /**
  * Custom hook for absolute session duration enforcement.
@@ -54,7 +48,7 @@ const DEFAULT_MAX_SESSION_DURATION_MS = (() => {
  *   When true and no session start exists, the current time is recorded.
  *   When false, the session start is cleared.
  * @param {number} [options.maxDurationMs] - Maximum session duration in 
milliseconds.
- *   Defaults to NEXT_PUBLIC_MAX_SESSION_DURATION_MS env var or 5 hours.
+ *   Defaults to 5 hours.
  * @returns {{ isExpired: boolean, remainingMs: number, clearSession: () => 
void }}
  */
 export function useAbsoluteSessionTimeout({ isAuthenticated, maxDurationMs = 
DEFAULT_MAX_SESSION_DURATION_MS } = {}) {
diff --git a/web-v2/web/src/lib/hooks/useIdleTimeout.js 
b/web-v2/web/src/lib/hooks/useIdleTimeout.js
index dc4246d748..56fb67a2a0 100644
--- a/web-v2/web/src/lib/hooks/useIdleTimeout.js
+++ b/web-v2/web/src/lib/hooks/useIdleTimeout.js
@@ -35,14 +35,8 @@ const DISABLED_SENTINEL = Number.MAX_SAFE_INTEGER
 
 /**
  * Default idle timeout: 15 minutes in milliseconds.
- * Overridable via NEXT_PUBLIC_IDLE_TIMEOUT_MS environment variable.
  */
-const DEFAULT_IDLE_TIMEOUT_MS = (() => {
-  const envVal = process.env.NEXT_PUBLIC_IDLE_TIMEOUT_MS
-  const parsed = envVal ? Number(envVal) : NaN
-
-  return Number.isFinite(parsed) && parsed > 0 ? parsed : 15 * 60 * 1000
-})()
+const DEFAULT_IDLE_TIMEOUT_MS = 15 * 60 * 1000
 
 /**
  * Throttle interval for activity events in milliseconds.
diff --git a/web-v2/web/src/lib/provider/IdleSessionProvider.js 
b/web-v2/web/src/lib/provider/IdleSessionProvider.js
index 0ffdcb8302..974909f2fe 100644
--- a/web-v2/web/src/lib/provider/IdleSessionProvider.js
+++ b/web-v2/web/src/lib/provider/IdleSessionProvider.js
@@ -28,28 +28,19 @@ import { useBroadcastChannel } from 
'@/lib/hooks/useBroadcastChannel'
 import { logoutAction } from '@/lib/store/auth'
 import IdleSessionContext from './IdleSessionContext'
 import IdleWarningModal from '@/components/IdleWarningModal'
+import { resolveSessionTimeouts } from './sessionTimeoutConfig'
 
 /**
  * Default idle timeout: 15 minutes in milliseconds.
- * Overridable via NEXT_PUBLIC_IDLE_TIMEOUT_MS environment variable.
  */
-const DEFAULT_IDLE_TIMEOUT_MS = (() => {
-  const envVal = process.env.NEXT_PUBLIC_IDLE_TIMEOUT_MS
-  const parsed = envVal ? Number(envVal) : NaN
-
-  return Number.isFinite(parsed) && parsed > 0 ? parsed : 15 * 60 * 1000
-})()
+const DEFAULT_IDLE_TIMEOUT_MS = 15 * 60 * 1000
 
 /**
  * Default warning lead time: 60 seconds in milliseconds.
- * Overridable via NEXT_PUBLIC_IDLE_WARNING_LEAD_MS environment variable.
  */
-const DEFAULT_WARNING_LEAD_MS = (() => {
-  const envVal = process.env.NEXT_PUBLIC_IDLE_WARNING_LEAD_MS
-  const parsed = envVal ? Number(envVal) : NaN
+const DEFAULT_WARNING_LEAD_MS = 60 * 1000
 
-  return Number.isFinite(parsed) && parsed > 0 ? parsed : 60 * 1000
-})()
+const DEFAULT_MAX_SESSION_DURATION_MS = 5 * 60 * 60 * 1000
 
 /**
  * Converts milliseconds to seconds, rounded up.
@@ -72,17 +63,26 @@ function msToSeconds(ms) {
  * @param {React.ReactNode} props.children
  * @param {number} [props.idleTimeoutMs] - Idle timeout in milliseconds 
(default: 15 min)
  * @param {number} [props.warningLeadMs] - Warning lead time in milliseconds 
(default: 60s)
+ * @param {number} [props.maxSessionDurationMs] - Maximum session duration 
(default: 5 hours)
  */
 export default function IdleSessionProvider({
   children,
   idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS,
-  warningLeadMs = DEFAULT_WARNING_LEAD_MS
+  warningLeadMs = DEFAULT_WARNING_LEAD_MS,
+  maxSessionDurationMs = DEFAULT_MAX_SESSION_DURATION_MS
 }) {
   const router = useRouter()
   const pathname = usePathname()
   const dispatch = useAppDispatch()
   const authType = useAppSelector(state => state.auth.authType)
   const authToken = useAppSelector(state => state.auth.authToken)
+  const systemConfig = useAppSelector(state => state.auth.systemConfig)
+
+  const {
+    idleTimeoutMs: resolvedIdleTimeoutMs,
+    warningLeadMs: resolvedWarningLeadMs,
+    maxSessionDurationMs: resolvedMaxSessionDurationMs
+  } = resolveSessionTimeouts(systemConfig, { idleTimeoutMs, warningLeadMs, 
maxSessionDurationMs })
 
   // Only enable idle timeout when the user is authenticated and not on login 
page.
   // Fall back to persisted token in localStorage to avoid prematurely treating
@@ -98,7 +98,7 @@ export default function IdleSessionProvider({
   const isLoginPage = pathname.endsWith('/login')
 
   const [state, setState] = useState('active')
-  const [warningCountdown, setWarningCountdown] = 
useState(msToSeconds(warningLeadMs))
+  const [warningCountdown, setWarningCountdown] = 
useState(msToSeconds(resolvedWarningLeadMs))
   const loggedOutRef = useRef(false)
   const wasAuthenticatedRef = useRef(isAuthenticated)
 
@@ -107,7 +107,7 @@ export default function IdleSessionProvider({
     resetActivity: resetIdleActivity,
     idleTimeRemaining
   } = useIdleTimeout({
-    idleTimeoutMs: isAuthenticated ? idleTimeoutMs : Number.MAX_SAFE_INTEGER,
+    idleTimeoutMs: isAuthenticated ? resolvedIdleTimeoutMs : 
Number.MAX_SAFE_INTEGER,
 
     // Pause DOM activity detection during warning state so the modal stays
     // visible until the user explicitly clicks "Stay signed in" or "Sign out 
now"
@@ -116,13 +116,14 @@ export default function IdleSessionProvider({
 
   // Absolute session duration: forces logout after a fixed time regardless of 
activity
   const { isExpired: isAbsoluteExpired } = useAbsoluteSessionTimeout({
-    isAuthenticated
+    isAuthenticated,
+    maxDurationMs: resolvedMaxSessionDurationMs
   })
 
   const { sendMessage, onMessage } = useBroadcastChannel()
 
   // Track warning threshold: when idleTimeRemaining drops below 
warningLeadMs, show warning
-  const warningThreshold = warningLeadMs
+  const warningThreshold = resolvedWarningLeadMs
 
   // State machine transitions
   useEffect(() => {
@@ -190,11 +191,11 @@ export default function IdleSessionProvider({
   const handleStaySignedIn = useCallback(() => {
     setState('active')
     resetIdleActivity()
-    setWarningCountdown(msToSeconds(warningLeadMs))
+    setWarningCountdown(msToSeconds(resolvedWarningLeadMs))
 
     // Broadcast stay_signed_in to other tabs so they also dismiss warning and 
stay logged in
     sendMessage({ type: 'stay_signed_in', timestamp: Date.now() })
-  }, [resetIdleActivity, warningLeadMs, sendMessage])
+  }, [resetIdleActivity, resolvedWarningLeadMs, sendMessage])
 
   // Cross-tab message handling (IST-REQ-005)
   useEffect(() => {
@@ -204,14 +205,14 @@ export default function IdleSessionProvider({
         resetIdleActivity()
         if (state === 'warning') {
           setState('active')
-          setWarningCountdown(msToSeconds(warningLeadMs))
+          setWarningCountdown(msToSeconds(resolvedWarningLeadMs))
         }
       } else if (message.type === 'stay_signed_in') {
         // Another tab clicked "Stay signed in" — dismiss warning and stay 
logged in
         resetIdleActivity()
         if (state === 'warning') {
           setState('active')
-          setWarningCountdown(msToSeconds(warningLeadMs))
+          setWarningCountdown(msToSeconds(resolvedWarningLeadMs))
         }
       } else if (message.type === 'logout') {
         // Another tab triggered logout — clear local auth state and redirect
@@ -230,7 +231,7 @@ export default function IdleSessionProvider({
         }
       }
     })
-  }, [onMessage, resetIdleActivity, router, state, warningLeadMs, dispatch])
+  }, [onMessage, resetIdleActivity, router, state, resolvedWarningLeadMs, 
dispatch])
 
   // Broadcast activity to other tabs when this tab has activity (IST-REQ-005)
   // Only broadcast when idleTimeRemaining jumps UP (indicating a timer reset),
@@ -275,7 +276,7 @@ export default function IdleSessionProvider({
       loggedOutRef.current = false
       setState('active')
       resetIdleActivity()
-      setWarningCountdown(msToSeconds(warningLeadMs))
+      setWarningCountdown(msToSeconds(resolvedWarningLeadMs))
     }
 
     // Broadcast authenticated→unauthenticated transition (e.g., manual logout 
from user menu)
@@ -285,7 +286,7 @@ export default function IdleSessionProvider({
     }
 
     wasAuthenticatedRef.current = isAuthenticated
-  }, [isAuthenticated, resetIdleActivity, sendMessage, warningLeadMs])
+  }, [isAuthenticated, resetIdleActivity, sendMessage, resolvedWarningLeadMs])
 
   // Context value
   const contextValue = {
diff --git a/web-v2/web/src/lib/provider/sessionTimeoutConfig.js 
b/web-v2/web/src/lib/provider/sessionTimeoutConfig.js
new file mode 100644
index 0000000000..6a7d0947ab
--- /dev/null
+++ b/web-v2/web/src/lib/provider/sessionTimeoutConfig.js
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const SESSION_IDLE_TIMEOUT_KEY = 
'gravitino.server.webserver.sessionIdleTimeoutMs'
+
+const SESSION_MAX_DURATION_KEY = 
'gravitino.server.webserver.sessionMaxDurationMs'
+
+const SESSION_IDLE_WARNING_LEAD_KEY = 
'gravitino.server.webserver.sessionIdleWarningLeadMs'
+
+function resolveSessionDuration(serverValue, envValue, defaultValue) {
+  const serverDuration = Number(serverValue)
+  if (Number.isFinite(serverDuration) && serverDuration > 0) {
+    return serverDuration
+  }
+
+  const envDuration = Number(envValue)
+
+  return Number.isFinite(envDuration) && envDuration > 0 ? envDuration : 
defaultValue
+}
+
+/**
+ * Resolves all UI session timeout settings.
+ *
+ * @param {Object} systemConfig values returned by the /configs endpoint
+ * @param {Object} defaults built-in fallback values
+ * @returns {{idleTimeoutMs: number, warningLeadMs: number, 
maxSessionDurationMs: number}}
+ */
+export function resolveSessionTimeouts(systemConfig, defaults) {
+  return {
+    idleTimeoutMs: resolveSessionDuration(
+      systemConfig?.[SESSION_IDLE_TIMEOUT_KEY],
+      process.env.NEXT_PUBLIC_IDLE_TIMEOUT_MS,
+      defaults.idleTimeoutMs
+    ),
+    warningLeadMs: resolveSessionDuration(
+      systemConfig?.[SESSION_IDLE_WARNING_LEAD_KEY],
+      process.env.NEXT_PUBLIC_IDLE_WARNING_LEAD_MS,
+      defaults.warningLeadMs
+    ),
+    maxSessionDurationMs: resolveSessionDuration(
+      systemConfig?.[SESSION_MAX_DURATION_KEY],
+      process.env.NEXT_PUBLIC_MAX_SESSION_DURATION_MS,
+      defaults.maxSessionDurationMs
+    )
+  }
+}

Reply via email to