Access permissions, seed permissions, security, session, api/session improved handling + speed across the site/UX improvements

This commit is contained in:
2026-05-08 00:00:56 +12:00
parent ebee72d4df
commit 1533b5aa9b
29 changed files with 1851 additions and 520 deletions
+24
View File
@@ -11,6 +11,10 @@ export type AppSession = {
user_id?: number | null;
client_account_id?: number | null;
module_permissions?: Record<string, string>;
// Permission-key array, populated when the user signed in via the internal
// Hunter Stock Feeds /api/access/login endpoint. Drives feature gating.
permissions?: string[];
role_name?: string | null;
};
const ACCESS_LEVEL_ORDER: Record<string, number> = {
@@ -63,6 +67,9 @@ function createSessionStore(storageKey: string) {
clear() {
if (browser) {
localStorage.removeItem(storageKey);
// Drop any cached API responses keyed to the old session token.
// Imported lazily so this module stays free of api.ts side-effects.
import('$lib/api').then(({ clearApiCache }) => clearApiCache()).catch(() => {});
}
store.set(null);
}
@@ -102,6 +109,23 @@ export function hasModuleAccess(
return (ACCESS_LEVEL_ORDER[currentLevel] ?? 0) >= ACCESS_LEVEL_ORDER[minimumLevel];
}
// Permission-key check for the internal access-control system. Returns false
// for legacy sessions that don't carry a permissions array. UI gating only —
// every privileged backend route still enforces permissions itself.
export function hasPermission(session: AppSession | null | undefined, permissionKey: string) {
if (!session?.permissions) {
return false;
}
return session.permissions.includes(permissionKey);
}
export function hasAnyPermission(session: AppSession | null | undefined, permissionKeys: string[]) {
if (!session?.permissions) {
return false;
}
return permissionKeys.some((key) => session.permissions!.includes(key));
}
export const sessionHydrated = readable(false, (set) => {
if (!browser) {
return undefined;