Files
Oxicloud/frontend/src/lib/utils/localStoragePrefs.ts
T
Edouard Vanbelle 3108fed228 fix(front): clean localStorage on user change
- fix issue with selected drive and user logout/login via another user
    (was raising a "404 not found")
    - normalize all localStorage to "oxi-" prefix
    - add a specific frontend/AGENTS.md for frontend part (stop increasing the global AGENTS.md)
2026-07-07 21:37:00 +02:00

82 lines
2.8 KiB
TypeScript

/**
* Local-storage prefs — key convention + user-scoped cleanup.
*
* # Key convention
*
* Every persistent client-side preference lives under the `oxi-` prefix.
* Historical mix of `oxicloud_*`, `oxicloud-*`, and `oxi-*` normalised to
* one form so `wipeAppKeys()` below can sweep the whole set with a single
* `startsWith('oxi-')` predicate.
*
* # Nuke-on-mismatch at login
*
* `ensureActiveUser(userId)` compares the newly-authenticated user id
* against the stored `oxi-active-user-id`. If they differ, EVERY `oxi-*`
* key is removed (except the active-user marker itself). This runs on:
* * first login after page load,
* * "switch account" flows where the current tab silently changes user,
* * session expiry then re-login as someone else.
*
* The wipe is intentionally broad — one naming convention beats maintaining
* a per-key whitelist that decays as new preferences get added.
*
* # Not stored here
*
* Auth tokens and CSRF cookies do NOT use `oxi-*` keys — they live in
* HTTP-only cookies set by the backend and are outside localStorage.
* Nothing to wipe there.
*/
/** Marker key: which user's prefs currently live in localStorage. */
const ACTIVE_USER_KEY = 'oxi-active-user-id';
/** Every persistent client pref key must start with this. */
const OXI_PREFIX = 'oxi-';
/**
* Remove every `oxi-*` key from localStorage EXCEPT the active-user marker.
* Idempotent; no-op when localStorage is unavailable (SSR, private mode).
*/
export function wipeAppKeys(): void {
if (typeof localStorage === 'undefined') return;
// Materialise the key list first — mutating localStorage while iterating
// its live view skips half the entries.
const keys = Object.keys(localStorage);
for (const key of keys) {
if (key === ACTIVE_USER_KEY) continue;
if (key.startsWith(OXI_PREFIX)) {
try {
localStorage.removeItem(key);
} catch {
/* private mode / quota — best-effort cleanup */
}
}
}
}
/**
* Ensure any localStorage state belongs to `userId`. When the marker
* doesn't match — first login of the page, re-login as someone else,
* or a fossil from a previous release with no marker — the whole app
* key namespace is nuked and the marker is set to the current user.
*
* Call once, right after the session store observes an authenticated
* user. Cheap when no work is needed (single `getItem`).
*/
export function ensureActiveUser(userId: string): void {
if (typeof localStorage === 'undefined') return;
let stored: string | null = null;
try {
stored = localStorage.getItem(ACTIVE_USER_KEY);
} catch {
/* private mode — treat as "no marker" so we do the cleanup pass */
}
if (stored === userId) return;
wipeAppKeys();
try {
localStorage.setItem(ACTIVE_USER_KEY, userId);
} catch {
/* private mode — cleanup still ran, marker will re-attempt next login */
}
}