apiFetch wrapper sends JSON with credentials, parses error
shapes off the backend's {error: '...'} responses, and exposes
typed helpers (apiGet, apiPost, apiDelete). Auth store now
drives the real /signup -> /confirm-email -> /login -> /me ->
/logout flow. Vite dev proxy points /api at the backend on
:8000.
104 lines
2.3 KiB
TypeScript
104 lines
2.3 KiB
TypeScript
import { defineStore } from "pinia";
|
|
import { computed, ref } from "vue";
|
|
import { apiGet, apiPost } from "@/api/client";
|
|
|
|
export interface AuthUser {
|
|
id: number;
|
|
email: string;
|
|
displayName: string;
|
|
isAdmin: boolean;
|
|
}
|
|
|
|
interface MePayload {
|
|
user: AuthUser;
|
|
}
|
|
|
|
export const useAuthStore = defineStore("auth", () => {
|
|
const user = ref<AuthUser | null>(null);
|
|
const checked = ref(false);
|
|
const error = ref<string | null>(null);
|
|
const signupCompleted = ref(false);
|
|
|
|
const isAuthenticated = computed(() => user.value !== null);
|
|
const isAdmin = computed(() => user.value?.isAdmin === true);
|
|
|
|
async function fetchMe(): Promise<boolean> {
|
|
const result = await apiGet<MePayload>("/me");
|
|
checked.value = true;
|
|
if (result.ok && result.data) {
|
|
user.value = result.data.user;
|
|
return true;
|
|
}
|
|
user.value = null;
|
|
return false;
|
|
}
|
|
|
|
async function signup(email: string, displayName: string): Promise<boolean> {
|
|
error.value = null;
|
|
const result = await apiPost<null>("/signup", {
|
|
email,
|
|
displayName,
|
|
});
|
|
if (!result.ok) {
|
|
error.value = result.error;
|
|
return false;
|
|
}
|
|
signupCompleted.value = true;
|
|
return true;
|
|
}
|
|
|
|
async function confirmEmail(token: string, password: string): Promise<boolean> {
|
|
error.value = null;
|
|
const result = await apiPost<null>("/confirm-email", {
|
|
token,
|
|
password,
|
|
});
|
|
if (!result.ok) {
|
|
error.value = result.error;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function login(email: string, password: string): Promise<boolean> {
|
|
error.value = null;
|
|
const result = await apiPost<MePayload>("/login", {
|
|
email,
|
|
password,
|
|
});
|
|
if (!result.ok) {
|
|
error.value = result.error;
|
|
return false;
|
|
}
|
|
if (result.data) {
|
|
user.value = result.data.user;
|
|
checked.value = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function logout(): Promise<void> {
|
|
await apiPost<null>("/logout", {});
|
|
user.value = null;
|
|
signupCompleted.value = false;
|
|
}
|
|
|
|
function clearError(): void {
|
|
error.value = null;
|
|
}
|
|
|
|
return {
|
|
user,
|
|
checked,
|
|
error,
|
|
signupCompleted,
|
|
isAuthenticated,
|
|
isAdmin,
|
|
fetchMe,
|
|
signup,
|
|
confirmEmail,
|
|
login,
|
|
logout,
|
|
clearError,
|
|
};
|
|
});
|