a5cef078f5
Persistent logins land in localStorage, ephemeral ones in sessionStorage, and we recall the last username + remember preference on the login form. Also propagate the indigo brand color through the antd ConfigProvider theme token so the side nav and avatar match the redesigned login.
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
const SESSION_KEY = "geo.ops-web.session";
|
|
const PREFERENCE_KEY = "geo.ops-web.login-preference";
|
|
|
|
export interface OperatorView {
|
|
id: number;
|
|
username: string;
|
|
display_name: string;
|
|
email: string | null;
|
|
role: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface OpsSession {
|
|
accessToken: string | null;
|
|
expiresAt: number | null;
|
|
operator: OperatorView | null;
|
|
}
|
|
|
|
export interface LoginPreference {
|
|
remember: boolean;
|
|
lastUsername: string;
|
|
}
|
|
|
|
export interface StoredSession {
|
|
session: OpsSession;
|
|
persistent: boolean;
|
|
}
|
|
|
|
function emptySession(): OpsSession {
|
|
return { accessToken: null, expiresAt: null, operator: null };
|
|
}
|
|
|
|
function emptyPreference(): LoginPreference {
|
|
return { remember: false, lastUsername: "" };
|
|
}
|
|
|
|
function hasWindow(): boolean {
|
|
return typeof window !== "undefined";
|
|
}
|
|
|
|
function readFrom(storage: Storage): OpsSession | null {
|
|
try {
|
|
const raw = storage.getItem(SESSION_KEY);
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(raw) as Partial<OpsSession>;
|
|
if (typeof parsed.accessToken !== "string" || !parsed.accessToken) return null;
|
|
return {
|
|
accessToken: parsed.accessToken,
|
|
expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : null,
|
|
operator:
|
|
parsed.operator && typeof parsed.operator === "object"
|
|
? (parsed.operator as OperatorView)
|
|
: null,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function readStoredSession(): StoredSession {
|
|
if (!hasWindow()) return { session: emptySession(), persistent: false };
|
|
const persistent = readFrom(window.localStorage);
|
|
if (persistent) return { session: persistent, persistent: true };
|
|
const ephemeral = readFrom(window.sessionStorage);
|
|
if (ephemeral) return { session: ephemeral, persistent: false };
|
|
return { session: emptySession(), persistent: false };
|
|
}
|
|
|
|
export function writeStoredSession(session: OpsSession, persistent: boolean): void {
|
|
if (!hasWindow()) return;
|
|
const target = persistent ? window.localStorage : window.sessionStorage;
|
|
const other = persistent ? window.sessionStorage : window.localStorage;
|
|
try {
|
|
target.setItem(SESSION_KEY, JSON.stringify(session));
|
|
} catch {
|
|
// ignore quota / serialization errors
|
|
}
|
|
try {
|
|
other.removeItem(SESSION_KEY);
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
export function clearStoredSession(): void {
|
|
if (!hasWindow()) return;
|
|
try {
|
|
window.localStorage.removeItem(SESSION_KEY);
|
|
} catch {
|
|
// noop
|
|
}
|
|
try {
|
|
window.sessionStorage.removeItem(SESSION_KEY);
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
export function readLoginPreference(): LoginPreference {
|
|
if (!hasWindow()) return emptyPreference();
|
|
try {
|
|
const raw = window.localStorage.getItem(PREFERENCE_KEY);
|
|
if (!raw) return emptyPreference();
|
|
const parsed = JSON.parse(raw) as Partial<LoginPreference>;
|
|
return {
|
|
remember: parsed.remember === true,
|
|
lastUsername: typeof parsed.lastUsername === "string" ? parsed.lastUsername : "",
|
|
};
|
|
} catch {
|
|
return emptyPreference();
|
|
}
|
|
}
|
|
|
|
export function writeLoginPreference(pref: LoginPreference): void {
|
|
if (!hasWindow()) return;
|
|
try {
|
|
window.localStorage.setItem(PREFERENCE_KEY, JSON.stringify(pref));
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|