1
0
forked from baron/baron-sso

테넌트 목록 조회 cursor기반으로 재구성. 사용자 metadata 미사용 필드 제거

This commit is contained in:
2026-05-13 18:05:51 +09:00
parent a4d707d4d8
commit 5e7b7b878c
85 changed files with 4808 additions and 734 deletions

View File

@@ -15,6 +15,7 @@ import { useEffect, useRef, useState } from "react";
import { useAuth } from "react-oidc-context";
import { NavLink, Outlet, useLocation, useNavigate } from "react-router-dom";
import {
type ShellTranslator,
applyShellTheme,
buildShellProfileSummary,
buildShellSessionStatus,
@@ -60,6 +61,48 @@ const navItems = [
},
];
type SessionStatusProps = {
expiresAtSec?: number | null;
t: ShellTranslator;
};
function useSessionStatus({ expiresAtSec, t }: SessionStatusProps) {
const [nowMs, setNowMs] = useState(() => Date.now());
useEffect(() => {
const timer = window.setInterval(() => {
setNowMs(Date.now());
}, 1000);
return () => {
window.clearInterval(timer);
};
}, []);
return buildShellSessionStatus({ expiresAtSec, nowMs, t });
}
function SessionStatusBadge(props: SessionStatusProps) {
const sessionStatus = useSessionStatus(props);
return (
<span
className={[
shellLayoutClasses.sessionBadge,
sessionStatus.toneClass,
].join(" ")}
>
{sessionStatus.text}
</span>
);
}
function SessionStatusText(props: SessionStatusProps) {
const sessionStatus = useSessionStatus(props);
return <>{sessionStatus.text}</>;
}
function AppLayout() {
const auth = useAuth();
const location = useLocation();
@@ -73,8 +116,6 @@ function AppLayout() {
const [isSessionExpiryEnabled, setIsSessionExpiryEnabled] = useState(
readShellSessionExpiryEnabled,
);
const [nowMs, setNowMs] = useState(() => Date.now());
const hasAccessToken = Boolean(auth.user?.access_token);
const { data: profile } = useQuery({
queryKey: ["userMe"],
@@ -97,15 +138,6 @@ function AppLayout() {
applyShellTheme(theme);
}, [theme]);
useEffect(() => {
const timer = window.setInterval(() => {
setNowMs(Date.now());
}, 1000);
return () => {
window.clearInterval(timer);
};
}, []);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
@@ -284,12 +316,6 @@ function AppLayout() {
auth.user?.profile as Record<string, unknown> | undefined,
);
const displayRoleKey = profile?.role || currentRole;
const sessionStatus = buildShellSessionStatus({
expiresAtSec: auth.user?.expires_at,
nowMs,
t,
});
const handleSessionExpiryToggle = () => {
setIsSessionExpiryEnabled((prev) => {
const next = !prev;
@@ -398,14 +424,10 @@ function AppLayout() {
: t("ui.common.theme_dark", "Dark")}
</button>
{isSessionExpiryEnabled ? (
<span
className={[
shellLayoutClasses.sessionBadge,
sessionStatus.toneClass,
].join(" ")}
>
{sessionStatus.text}
</span>
<SessionStatusBadge
expiresAtSec={auth.user?.expires_at}
t={t}
/>
) : null}
<div className="relative" ref={profileMenuRef}>
<button
@@ -466,12 +488,17 @@ function AppLayout() {
{t("ui.dev.session.auto_extend", "Session expiry")}
</p>
<p className="text-xs text-muted-foreground">
{isSessionExpiryEnabled
? sessionStatus.text
: t(
"ui.dev.session.disabled",
"Session expiry disabled",
)}
{isSessionExpiryEnabled ? (
<SessionStatusText
expiresAtSec={auth.user?.expires_at}
t={t}
/>
) : (
t(
"ui.dev.session.disabled",
"Session expiry disabled",
)
)}
</p>
</div>
<button

View File

@@ -1,6 +1,9 @@
import axios from "axios";
import { shouldStartLoginRedirect } from "../../../common/core/auth";
import { userManager } from "./auth";
let isRedirectingToLogin = false;
const apiClient = axios.create({
baseURL:
import.meta.env.VITE_DEV_API_BASE ??
@@ -32,8 +35,6 @@ apiClient.interceptors.response.use(
error.response?.data?.error?.toString().toLowerCase() ??
error.response?.data?.message?.toString().toLowerCase() ??
"";
const isAuthPath = window.location.pathname.startsWith("/auth/callback");
const isLoginPath = window.location.pathname === "/login";
const shouldRedirectToLogin =
status === 401 ||
(status === 403 &&
@@ -41,7 +42,14 @@ apiClient.interceptors.response.use(
message.includes("invalid session") ||
message.includes("token is not active")));
if (shouldRedirectToLogin && !isAuthPath && !isLoginPath) {
if (
shouldRedirectToLogin &&
shouldStartLoginRedirect({
pathname: window.location.pathname,
isRedirecting: isRedirectingToLogin,
})
) {
isRedirectingToLogin = true;
await userManager.removeUser();
window.location.href = "/login";
}