ФУНКЦИИ - NODEDC LAUNCHER: sync logout across tabs
This commit is contained in:
+17
-1
@@ -42,6 +42,7 @@ import {
|
||||
type LauncherAuthApp,
|
||||
} from "../shared/api/authApi";
|
||||
import { updateOwnPassword, updateOwnProfile } from "../shared/api/profileApi";
|
||||
import { subscribeToNodeDCLogoutEvents } from "../shared/session/sessionSync";
|
||||
import { loadPersistedLauncherData } from "../shared/api/storageApi";
|
||||
import {
|
||||
AdminOverlay,
|
||||
@@ -190,6 +191,17 @@ export function LauncherApp() {
|
||||
window.location.replace(buildLoginRedirectUrl(authSession.loginUrl));
|
||||
}, [authSession]);
|
||||
|
||||
useEffect(() => {
|
||||
let isRedirecting = false;
|
||||
|
||||
return subscribeToNodeDCLogoutEvents(() => {
|
||||
if (isRedirecting) return;
|
||||
|
||||
isRedirecting = true;
|
||||
window.location.replace(buildLoginRedirectUrl("/auth/login?prompt=login"));
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
@@ -511,6 +523,10 @@ export function LauncherApp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
window.location.replace(authSession.logoutUrl);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="launcher-app">
|
||||
<TopBar
|
||||
@@ -525,7 +541,7 @@ export function LauncherApp() {
|
||||
onToggleAdmin={() => setAdminOpen((current) => !current)}
|
||||
onOpenShowcase={() => setAdminOpen(false)}
|
||||
onOpenProfileSettings={() => setProfileSettingsOpen(true)}
|
||||
onLogout={() => window.location.replace(authSession.logoutUrl)}
|
||||
onLogout={handleLogout}
|
||||
/>
|
||||
|
||||
<main className="launcher-main">
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
export const NODEDC_SESSION_EVENT_TYPE = "nodedc:session:logout";
|
||||
export const NODEDC_SESSION_CHANNEL_NAME = "nodedc-platform-session";
|
||||
export const NODEDC_SESSION_STORAGE_KEY = "nodedc:platform-session-event";
|
||||
|
||||
export interface NodeDCSessionEvent {
|
||||
type: typeof NODEDC_SESSION_EVENT_TYPE;
|
||||
id: string;
|
||||
source: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export function publishNodeDCLogoutEvent(source = "launcher") {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const event: NodeDCSessionEvent = {
|
||||
type: NODEDC_SESSION_EVENT_TYPE,
|
||||
id: createEventId(),
|
||||
source,
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
const channel = new BroadcastChannel(NODEDC_SESSION_CHANNEL_NAME);
|
||||
channel.postMessage(event);
|
||||
channel.close();
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
|
||||
try {
|
||||
window.localStorage.setItem(NODEDC_SESSION_STORAGE_KEY, JSON.stringify(event));
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeToNodeDCLogoutEvents(onLogout: (event: NodeDCSessionEvent) => void) {
|
||||
if (typeof window === "undefined") return () => undefined;
|
||||
|
||||
let lastEventId: string | null = null;
|
||||
let channel: BroadcastChannel | null = null;
|
||||
|
||||
const handleEvent = (payload: unknown) => {
|
||||
if (!isNodeDCSessionEvent(payload) || payload.id === lastEventId) return;
|
||||
|
||||
lastEventId = payload.id;
|
||||
onLogout(payload);
|
||||
};
|
||||
|
||||
try {
|
||||
channel = new BroadcastChannel(NODEDC_SESSION_CHANNEL_NAME);
|
||||
channel.addEventListener("message", (event) => handleEvent(event.data));
|
||||
} catch {
|
||||
channel = null;
|
||||
}
|
||||
|
||||
const handleStorage = (event: StorageEvent) => {
|
||||
if (event.key !== NODEDC_SESSION_STORAGE_KEY || !event.newValue) return;
|
||||
|
||||
try {
|
||||
handleEvent(JSON.parse(event.newValue));
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("storage", handleStorage);
|
||||
|
||||
return () => {
|
||||
channel?.close();
|
||||
window.removeEventListener("storage", handleStorage);
|
||||
};
|
||||
}
|
||||
|
||||
function isNodeDCSessionEvent(value: unknown): value is NodeDCSessionEvent {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
|
||||
const event = value as Partial<NodeDCSessionEvent>;
|
||||
return (
|
||||
event.type === NODEDC_SESSION_EVENT_TYPE &&
|
||||
typeof event.id === "string" &&
|
||||
typeof event.source === "string" &&
|
||||
typeof event.createdAt === "number"
|
||||
);
|
||||
}
|
||||
|
||||
function createEventId() {
|
||||
try {
|
||||
return crypto.randomUUID();
|
||||
} catch {
|
||||
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user