155 lines
5.7 KiB
TypeScript
155 lines
5.7 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import NavLayout from "./NavLayout.tsx";
|
|
import { Route, Routes, useNavigate } from "react-router-dom";
|
|
import Home from "../Home.tsx";
|
|
import './MainLayout.css';
|
|
import { validateToken, GetUserPrivileges, TokenPayload, UnAuthorized } from '../Auth/Auth.tsx';
|
|
import '../LoadingScreen.css'
|
|
import AccountDropdown from "./AccountDropdown.tsx";
|
|
import AccountSettings from "../Auth/AccountSetting.tsx";
|
|
import { Api as Auth } from '../client/auth_client.ts';
|
|
import '../Pages/NotFound.tsx';
|
|
import NotFound from "../Pages/NotFound.tsx";
|
|
import "../Pages/UserAccountList.tsx";
|
|
import UserAccountList from "../Pages/UserAccountList.tsx";
|
|
|
|
const MainLayout: React.FC = () => {
|
|
const [tokenPayload, setTokenPayload] = useState<TokenPayload | null>(null);
|
|
const [tokenExpiryTime, setTokenExpiryTime] = useState<number | null>(null);
|
|
const [isAuthenticating, setIsAuthenticating] = useState(true);
|
|
const [privilegesList, setPrivilegesList] = useState<string[]>();
|
|
const [isTokenRegenerating, setIsTokenRegenerating] = useState<boolean>(false);
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
const validateAndSetToken = async () => {
|
|
if (isTokenRegenerating) return;
|
|
|
|
const token = localStorage.getItem('authToken');
|
|
if (token) {
|
|
const payload = await validateToken(token);
|
|
if (payload) {
|
|
setTokenPayload(payload);
|
|
setTokenExpiryTime(payload.exp);
|
|
setIsAuthenticating(false);
|
|
} else {
|
|
localStorage.removeItem('authToken');
|
|
navigate('/login');
|
|
}
|
|
} else {
|
|
navigate('/login');
|
|
}
|
|
};
|
|
|
|
const getUserPrivileges = async () => {
|
|
const privileges = await GetUserPrivileges(navigate);
|
|
setPrivilegesList(privileges);
|
|
};
|
|
|
|
getUserPrivileges().then();
|
|
validateAndSetToken().then();
|
|
}, [navigate, isTokenRegenerating]);
|
|
|
|
useEffect(() => {
|
|
if (tokenExpiryTime) {
|
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
const timeUntilExpiry = tokenExpiryTime - currentTime;
|
|
|
|
if (timeUntilExpiry > 900) {
|
|
const timeoutId = setTimeout(() => {
|
|
regenerateToken().then();
|
|
}, (timeUntilExpiry - 900) * 1000);
|
|
return () => clearTimeout(timeoutId);
|
|
} else {
|
|
regenerateToken().then();
|
|
}
|
|
}
|
|
}, [tokenExpiryTime]);
|
|
|
|
const regenerateToken = async () => {
|
|
if (isTokenRegenerating) return;
|
|
|
|
setIsTokenRegenerating(true);
|
|
const token = localStorage.getItem('authToken');
|
|
if (token) {
|
|
const client = new Auth({
|
|
baseUrl: 'auth',
|
|
securityWorker: () => ({ headers: { Authorization: `Bearer ${token}` } }),
|
|
});
|
|
try {
|
|
const responseToken = await client.regenerateToken.regenerateTokenList();
|
|
if (responseToken.status === 401) {
|
|
await UnAuthorized(navigate);
|
|
} else if (responseToken.data.token) {
|
|
const newToken = responseToken.data.token;
|
|
localStorage.setItem('authToken', newToken);
|
|
const newPayload = await validateToken(newToken);
|
|
if (newPayload) {
|
|
setTokenPayload(newPayload);
|
|
setTokenExpiryTime(newPayload.exp);
|
|
} else {
|
|
await validateToken(newToken);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Token regeneration failed', error);
|
|
localStorage.removeItem('authToken');
|
|
navigate('/login');
|
|
} finally {
|
|
setIsTokenRegenerating(false);
|
|
}
|
|
} else {
|
|
setIsTokenRegenerating(false);
|
|
navigate('/login');
|
|
}
|
|
};
|
|
|
|
if (isAuthenticating) {
|
|
return (
|
|
<div className="loading-screen">
|
|
<div className="spinner">
|
|
<div className="lds-ring">
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="page">
|
|
<div className="sidebar">
|
|
<NavLayout privilegeList={privilegesList} />
|
|
</div>
|
|
|
|
<main className="main-content">
|
|
<div className="top-row px-4">
|
|
{tokenPayload && (
|
|
<AccountDropdown
|
|
tokenPayload={tokenPayload}
|
|
privilegeList={privilegesList}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="content px-4">
|
|
<Routes>
|
|
<Route path="/" element={<Home tokenPayload={tokenPayload} />} />
|
|
|
|
<Route
|
|
path="/accountSettings/"
|
|
element={<AccountSettings />}
|
|
/>
|
|
<Route path="/accounts/" element={<UserAccountList />} />
|
|
<Route path="/*" element={<NotFound />} />
|
|
</Routes>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MainLayout;
|