feat(web): add rune-based auth store with bootstrap/login/logout
Central synchronous source of truth for 'who is signed in'. bootstrap() runs once in +layout.ts load(); login/logout mutate _user and the TanStack query cache. The silent option on logout is used by the 401 interceptor (next commit) to avoid POSTing /logout against an already- invalid session.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { api, type User, type LoginResponse } from '$lib/api/client';
|
||||
import { queryClient } from '$lib/query/client';
|
||||
|
||||
let _user = $state<User | null>(null);
|
||||
|
||||
export const user = {
|
||||
get value(): User | null {
|
||||
return _user;
|
||||
}
|
||||
};
|
||||
|
||||
export async function bootstrap(): Promise<void> {
|
||||
try {
|
||||
_user = await api.get<User>('/api/me');
|
||||
} catch {
|
||||
_user = null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function login(username: string, password: string): Promise<void> {
|
||||
const res = await api.post<LoginResponse>('/api/auth/login', { username, password });
|
||||
_user = res.user;
|
||||
}
|
||||
|
||||
export async function logout(opts: { silent?: boolean } = {}): Promise<void> {
|
||||
if (!opts.silent) {
|
||||
try {
|
||||
await api.post('/api/auth/logout', {});
|
||||
} catch {
|
||||
// best effort — server-side session may already be gone
|
||||
}
|
||||
}
|
||||
_user = null;
|
||||
queryClient.clear();
|
||||
}
|
||||
Reference in New Issue
Block a user