feat: entity cache system, nav UX improvements, split CLAUDE.md
- Add $state-based entity cache layer with 30s TTL, request deduplication, and local mutation helpers (entity-cache.svelte.ts + caches.svelte.ts) - Wire all 10 page components to use shared caches for cross-page data - Add slide animation for nav tree expand/collapse with rotating chevron - Remove aggregate count badges from container nav nodes (keep on leaves) - Convert Targets from flat leaf to group with per-type children (Telegram, Webhook, Email, Discord, Slack, ntfy, Matrix) - Add URL-based type filtering on Targets page with per-type descriptions - Add Bots group children for Email and Matrix alongside Telegram - Tab-based routing for bots page (?tab=telegram/email/matrix) - Add per-type target counts and email/matrix bot counts to /status/counts - Split CLAUDE.md into focused context files under .claude/docs/ - Fix .gitignore: scope lib/ to root, allow .claude/docs/ tracking - Clear all caches on logout - Reset form state when switching target type tabs
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Reactive auth state using Svelte 5 runes.
|
||||
*/
|
||||
|
||||
import { api, setTokens, clearTokens, isAuthenticated } from './api';
|
||||
import { clearAllCaches } from './stores/caches.svelte';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
let user = $state<User | null>(null);
|
||||
let loading = $state(true);
|
||||
|
||||
export function getAuth() {
|
||||
return {
|
||||
get user() { return user; },
|
||||
get loading() { return loading; },
|
||||
get isAdmin() { return user?.role === 'admin'; },
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadUser() {
|
||||
if (!isAuthenticated()) {
|
||||
user = null;
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
user = await api<User>('/auth/me');
|
||||
} catch {
|
||||
user = null;
|
||||
clearTokens();
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(username: string, password: string) {
|
||||
const data = await api<{ access_token: string; refresh_token: string }>('/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
setTokens(data.access_token, data.refresh_token);
|
||||
await loadUser();
|
||||
}
|
||||
|
||||
export async function setup(username: string, password: string) {
|
||||
const data = await api<{ access_token: string; refresh_token: string }>('/auth/setup', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
setTokens(data.access_token, data.refresh_token);
|
||||
await loadUser();
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
clearTokens();
|
||||
clearAllCaches();
|
||||
user = null;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user