feat: harden sessions and track downstream acknowledgements

This commit is contained in:
hectorzhao
2026-07-14 14:18:43 +08:00
parent 3d37adcc9f
commit 8c03663f24
43 changed files with 1733 additions and 150 deletions
+45 -2
View File
@@ -12,10 +12,13 @@ export type SessionUser = {
};
export type LoginSession = {
accessToken: string;
tokenType: string;
portal: Portal;
user: SessionUser;
idleTimeoutSeconds: number;
lockRecoverySeconds: number;
absoluteExpiresAt: string;
lastActivityAt: string;
recentAuthenticationExpiresAt: string;
};
const sessionKey = 'cmpp-auth-session';
@@ -37,6 +40,46 @@ export function clearSession() {
window.localStorage.removeItem(sessionKey);
}
export function updateSessionTiming(timing: Partial<Omit<LoginSession, 'portal' | 'user'>>) {
const current = readSession();
if (current) writeSession({ ...current, ...timing });
}
let lastUserActivityAt = Date.now();
let reauthenticationHandler: (() => Promise<void>) | undefined;
export function markUserActivity() {
lastUserActivityAt = Date.now();
}
export function getLastUserActivityAt() {
return lastUserActivityAt;
}
export function hasRecentUserActivity() {
return Date.now() - lastUserActivityAt < 60_000;
}
export function setReauthenticationHandler(handler?: () => Promise<void>) {
reauthenticationHandler = handler;
}
export function requestReauthentication() {
if (!reauthenticationHandler) return Promise.reject(new Error('请重新验证当前密码后再操作'));
return reauthenticationHandler();
}
export function dispatchSessionEvent(type: 'locked' | 'unlocked' | 'logout', detail?: Record<string, unknown>) {
window.dispatchEvent(new CustomEvent(`cmpp-session-${type}`, { detail }));
try {
const channel = new BroadcastChannel('cmpp-session');
channel.postMessage({ type, detail });
channel.close();
} catch {
// BroadcastChannel is an enhancement; the current tab still receives the DOM event.
}
}
export function getSessionTenantId() {
return readSession()?.user.tenantId ?? undefined;
}