Session validation
Build and Push Lunchtime Images (Kaniko) / build-and-push (push) Successful in 1m16s
Build and Push Lunchtime Images (Kaniko) / build-and-push (push) Successful in 1m16s
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
clearUserId,
|
||||
ensureUserId,
|
||||
hasStoredUserId,
|
||||
USER_SESSION_INVALIDATED_EVENT,
|
||||
updateUserId,
|
||||
} from "./lib/userIdentity";
|
||||
import { THEME_MODE_KEY } from "./lib/constants";
|
||||
@@ -363,6 +364,28 @@ export default function App() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleSessionInvalidated = () => {
|
||||
setUserId("");
|
||||
setUserEmail("");
|
||||
setIsOnboardingOpen(true);
|
||||
navigateTo("/");
|
||||
message.warning("Your session is no longer valid. Please sign in again.");
|
||||
};
|
||||
|
||||
window.addEventListener(
|
||||
USER_SESSION_INVALIDATED_EVENT,
|
||||
handleSessionInvalidated,
|
||||
);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener(
|
||||
USER_SESSION_INVALIDATED_EVENT,
|
||||
handleSessionInvalidated,
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const themeConfig = useMemo(
|
||||
() => ({
|
||||
algorithm: themeMode === "dark" ? darkAlgorithm : defaultAlgorithm,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ensureUserId } from "./userIdentity";
|
||||
import { ensureUserId, invalidateUserSession } from "./userIdentity";
|
||||
import type { ApiError } from "./types";
|
||||
|
||||
const API_BASE = "/api";
|
||||
@@ -17,6 +17,15 @@ export function isNotFoundError(error: unknown) {
|
||||
return !!(maybeError && (maybeError.isNotFound || maybeError.status === 404));
|
||||
}
|
||||
|
||||
function shouldClearStoredUserId(status: number, detail: unknown) {
|
||||
if (status !== 401) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const normalizedDetail = String(detail || "").trim().toLowerCase();
|
||||
return normalizedDetail === "invalid user id" || normalizedDetail === "missing user id";
|
||||
}
|
||||
|
||||
export async function apiFetch<T = any>(url: string, options: RequestInit = {}): Promise<T> {
|
||||
const fullUrl = url.startsWith("/api") ? API_BASE + url.slice(4) : url;
|
||||
const userId = ensureUserId();
|
||||
@@ -45,6 +54,10 @@ export async function apiFetch<T = any>(url: string, options: RequestInit = {}):
|
||||
|
||||
if (!response.ok) {
|
||||
const payload = await response.json().catch(() => ({} as { detail?: string }));
|
||||
if (shouldClearStoredUserId(response.status, payload?.detail)) {
|
||||
invalidateUserSession();
|
||||
}
|
||||
|
||||
throw createApiError(payload.detail || `Request failed (${response.status})`, {
|
||||
status: response.status,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { USER_TOKEN_KEY } from "./constants";
|
||||
import { getStoredValue, removeStoredValue, setStoredValue } from "./storage";
|
||||
|
||||
const emailByUserId = new Map<string, string>();
|
||||
export const USER_SESSION_INVALIDATED_EVENT = "lunchtime:user-session-invalidated";
|
||||
|
||||
function generateGuid() {
|
||||
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
||||
@@ -76,3 +77,11 @@ export function regenerateUserId() {
|
||||
export function clearUserId() {
|
||||
removeStoredValue(USER_TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function invalidateUserSession() {
|
||||
clearUserId();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new Event(USER_SESSION_INVALIDATED_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user