Extends api.ts with the Phase 3.5 wire types (InstanceRole, Scope, MeDto, AdminDto, ApiKeyDto, MintApiKey*) and the matching apiKeys namespace. AdminUser in auth.ts now carries instance_role and email, so layout/store consumers see the role without a separate fetch. Adds two tiny lib helpers used by the upcoming profile/users pages: RoleChip.svelte for the colored owner/admin/member pill, and password-gen.ts for crypto.getRandomValues-backed temporary passwords used in user-invite + reset-password reveals. AdminUserRecord stays as a deprecated alias until /admins is retired in a follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
// Session state for the dashboard. Backed by a pair of Svelte stores
|
|
// plus a tiny localStorage echo so a page reload doesn't sign you out.
|
|
//
|
|
// The bearer token doubles as the cookie value on the server side, so
|
|
// in browsers that honor the Set-Cookie response the cookie path "just
|
|
// works"; the token-in-localStorage path covers the rest (HTTP dev, API
|
|
// clients impersonating the dashboard) by being injected into the
|
|
// Authorization header in api.ts.
|
|
|
|
import { writable, get } from 'svelte/store';
|
|
import { browser } from '$app/environment';
|
|
|
|
export type InstanceRole = 'owner' | 'admin' | 'member';
|
|
|
|
export interface AdminUser {
|
|
id: string;
|
|
username: string;
|
|
instance_role: InstanceRole;
|
|
email: string | null;
|
|
}
|
|
|
|
const TOKEN_KEY = 'picloud.admin.token';
|
|
|
|
function readStoredToken(): string | null {
|
|
if (!browser) return null;
|
|
try {
|
|
return localStorage.getItem(TOKEN_KEY);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function writeStoredToken(value: string | null) {
|
|
if (!browser) return;
|
|
try {
|
|
if (value === null) localStorage.removeItem(TOKEN_KEY);
|
|
else localStorage.setItem(TOKEN_KEY, value);
|
|
} catch {
|
|
// Non-fatal: localStorage can be disabled. The session will
|
|
// just not survive page reloads, but the in-memory store still
|
|
// works for the current SPA lifetime.
|
|
}
|
|
}
|
|
|
|
export const token = writable<string | null>(readStoredToken());
|
|
export const currentUser = writable<AdminUser | null>(null);
|
|
|
|
token.subscribe((value) => writeStoredToken(value));
|
|
|
|
/** Snapshot of the current token without subscribing — used by the
|
|
* fetch wrapper. Returns null when no admin is logged in. */
|
|
export function getToken(): string | null {
|
|
return get(token);
|
|
}
|
|
|
|
export function setSession(user: AdminUser, raw_token: string) {
|
|
currentUser.set(user);
|
|
token.set(raw_token);
|
|
}
|
|
|
|
export function clearSession() {
|
|
currentUser.set(null);
|
|
token.set(null);
|
|
}
|