- config store (public /api/config: site_name, allow_registration, version), loaded in the router guard; site name drives the board header. - session User gains is_admin. - /settings route with requiresAdmin guard + admin-only gear link in the header; SettingsView renders grouped typed fields (text/number/toggle), saves via PATCH /api/settings with saved/error feedback, refreshes public config. - Register link hidden + /register route blocked when registration is closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
47 lines
1.2 KiB
TypeScript
47 lines
1.2 KiB
TypeScript
import { defineStore } from "pinia";
|
|
import { ref } from "vue";
|
|
import { api } from "../api/client";
|
|
|
|
export interface User {
|
|
id: string;
|
|
email: string;
|
|
display_name: string;
|
|
email_verified: boolean;
|
|
is_admin: boolean;
|
|
}
|
|
|
|
export const useSessionStore = defineStore("session", () => {
|
|
const user = ref<User | null>(null);
|
|
// Whether we've resolved the initial /me check yet (guards against redirect flashes).
|
|
const loaded = ref(false);
|
|
|
|
async function fetchMe(): Promise<void> {
|
|
try {
|
|
user.value = await api.get<User>("/api/auth/me");
|
|
} catch {
|
|
user.value = null;
|
|
} finally {
|
|
loaded.value = true;
|
|
}
|
|
}
|
|
|
|
async function login(email: string, password: string): Promise<void> {
|
|
user.value = await api.post<User>("/api/auth/login", { email, password });
|
|
}
|
|
|
|
async function register(email: string, password: string, displayName: string): Promise<void> {
|
|
user.value = await api.post<User>("/api/auth/register", {
|
|
email,
|
|
password,
|
|
display_name: displayName,
|
|
});
|
|
}
|
|
|
|
async function logout(): Promise<void> {
|
|
await api.post("/api/auth/logout");
|
|
user.value = null;
|
|
}
|
|
|
|
return { user, loaded, fetchMe, login, register, logout };
|
|
});
|