session timeout

This commit is contained in:
2025-02-09 12:26:49 +08:00
parent 47bdb719c2
commit e7c92f252f
4 changed files with 64 additions and 1 deletions

View File

@@ -0,0 +1,57 @@
import React, { useState, useEffect, useCallback } from "react";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
interface SessionTimeoutProps {
timeout: number;
onLogout: () => void;
}
const SessionTimeout: React.FC<SessionTimeoutProps> = ({
timeout,
onLogout,
}) => {
const [lastActivityTime, setLastActivityTime] = useState<number>(Date.now());
const [notified, setNotified] = useState<boolean>(false);
const resetTimer = useCallback(() => {
setLastActivityTime(Date.now());
setNotified(false);
}, []);
useEffect(() => {
const events = ["click", "mousemove", "keypress", "scroll", "touchstart"];
const eventHandler = () => resetTimer();
events.forEach((event) => window.addEventListener(event, eventHandler));
return () => {
events.forEach((event) =>
window.removeEventListener(event, eventHandler)
);
};
}, [resetTimer]);
useEffect(() => {
const interval = setInterval(() => {
const timeElapsed = Date.now() - lastActivityTime;
if (timeElapsed >= timeout) {
onLogout();
} else if (timeElapsed >= timeout - 30000 && !notified) {
toast.warn("30 more seconds before automatic logout from idling.");
setNotified(true);
}
}, 1000);
return () => clearInterval(interval);
}, [lastActivityTime, timeout, onLogout, notified]);
return (
<>
<ToastContainer />
</>
);
};
export default SessionTimeout;

View File

@@ -48,7 +48,7 @@ export function login(token: string) {
export function logout() {
clearAccessToken();
window.location.reload();
window.location.assign("/");
}
export function getAccessToken() {

View File

@@ -6,6 +6,8 @@ import App from "./App.tsx";
import "./index.css";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import SessionTimeout from "./components/SessionTimeout.tsx";
import { getAccessToken, logout } from "./http.ts";
document.addEventListener("contextmenu", (event) => {
event.preventDefault();
@@ -18,6 +20,9 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<main>
<App />
<ToastContainer />
{getAccessToken() && (
<SessionTimeout timeout={1 * 60 * 1000} onLogout={logout} />
)}
</main>
</HeroUIProvider>
</BrowserRouter>

View File

@@ -16,6 +16,7 @@ export default function EditProfilePage() {
const accessToken = getAccessToken();
if (!accessToken) {
navigate(-1);
return;
}
http.get("/User/profile").then((response) => {
if (response.status !== 200) {