forked from baron/baron-sso
adminfront/devfront 세션 만료 관리 슬라이딩 갱신 로직 추가
This commit is contained in:
@@ -16,9 +16,10 @@ import {
|
||||
import * as React from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useAuth } from "react-oidc-context";
|
||||
import { NavLink, Outlet, useNavigate } from "react-router-dom";
|
||||
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import { fetchMe } from "../../lib/adminApi";
|
||||
import { t } from "../../lib/i18n";
|
||||
import { shouldAttemptSlidingSessionRenew } from "../../lib/sessionSliding";
|
||||
import LanguageSelector from "../common/LanguageSelector";
|
||||
import RoleSwitcher from "./RoleSwitcher";
|
||||
|
||||
@@ -32,8 +33,12 @@ const staticNavItems = [
|
||||
|
||||
function AppLayout() {
|
||||
const auth = useAuth();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const profileMenuRef = useRef<HTMLDivElement>(null);
|
||||
const isRenewInFlightRef = useRef(false);
|
||||
const lastRenewAttemptAtRef = useRef(0);
|
||||
const lastVisitedRouteRef = useRef<string | null>(null);
|
||||
const isDevRoleOverrideEnabled =
|
||||
import.meta.env.MODE === "development" ||
|
||||
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })
|
||||
@@ -169,6 +174,104 @@ function AppLayout() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const maybeRenewSession = async () => {
|
||||
const now = Date.now();
|
||||
if (
|
||||
!shouldAttemptSlidingSessionRenew({
|
||||
expiresAtSec: auth.user?.expires_at,
|
||||
nowMs: now,
|
||||
isEnabled: isSessionExpiryEnabled,
|
||||
isAuthenticated: auth.isAuthenticated,
|
||||
isLoading: auth.isLoading,
|
||||
isRenewInFlight: isRenewInFlightRef.current,
|
||||
lastAttemptAtMs: lastRenewAttemptAtRef.current,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
isRenewInFlightRef.current = true;
|
||||
lastRenewAttemptAtRef.current = now;
|
||||
|
||||
try {
|
||||
await auth.signinSilent();
|
||||
} catch (error) {
|
||||
console.error("세션 자동 연장에 실패했습니다.", error);
|
||||
} finally {
|
||||
isRenewInFlightRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleUserAction = () => {
|
||||
void maybeRenewSession();
|
||||
};
|
||||
|
||||
window.addEventListener("pointerdown", handleUserAction);
|
||||
window.addEventListener("keydown", handleUserAction);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("pointerdown", handleUserAction);
|
||||
window.removeEventListener("keydown", handleUserAction);
|
||||
};
|
||||
}, [
|
||||
auth,
|
||||
auth.isAuthenticated,
|
||||
auth.isLoading,
|
||||
auth.user?.expires_at,
|
||||
isSessionExpiryEnabled,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const routeKey = `${location.pathname}${location.search}${location.hash}`;
|
||||
if (lastVisitedRouteRef.current === null) {
|
||||
lastVisitedRouteRef.current = routeKey;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastVisitedRouteRef.current === routeKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastVisitedRouteRef.current = routeKey;
|
||||
|
||||
const now = Date.now();
|
||||
if (
|
||||
!shouldAttemptSlidingSessionRenew({
|
||||
expiresAtSec: auth.user?.expires_at,
|
||||
nowMs: now,
|
||||
isEnabled: isSessionExpiryEnabled,
|
||||
isAuthenticated: auth.isAuthenticated,
|
||||
isLoading: auth.isLoading,
|
||||
isRenewInFlight: isRenewInFlightRef.current,
|
||||
lastAttemptAtMs: lastRenewAttemptAtRef.current,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
isRenewInFlightRef.current = true;
|
||||
lastRenewAttemptAtRef.current = now;
|
||||
|
||||
void auth
|
||||
.signinSilent()
|
||||
.catch((error) => {
|
||||
console.error("세션 자동 연장에 실패했습니다.", error);
|
||||
})
|
||||
.finally(() => {
|
||||
isRenewInFlightRef.current = false;
|
||||
});
|
||||
}, [
|
||||
auth,
|
||||
auth.isAuthenticated,
|
||||
auth.isLoading,
|
||||
auth.user?.expires_at,
|
||||
isSessionExpiryEnabled,
|
||||
location.hash,
|
||||
location.pathname,
|
||||
location.search,
|
||||
]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme((prev) => (prev === "light" ? "dark" : "light"));
|
||||
};
|
||||
@@ -397,14 +500,14 @@ function AppLayout() {
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
{t("ui.dev.session.expired", "세션 만료")}
|
||||
{t("ui.dev.session.auto_extend", "세션 만료 관리")}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{isSessionExpiryEnabled
|
||||
? sessionText
|
||||
: t(
|
||||
"ui.dev.session.disabled",
|
||||
"세션 만료 표시 비활성화",
|
||||
"세션 만료 비활성화",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user