diff --git a/web/src/composables/useSession.ts b/web/src/composables/useSession.ts new file mode 100644 index 0000000..97ec892 --- /dev/null +++ b/web/src/composables/useSession.ts @@ -0,0 +1,84 @@ +import { ref, computed, readonly } from "vue"; + +interface User { + id: string; + username: string; +} + +const currentUser = ref(null); +const needsSetup = ref(null); // null = unknown / not fetched yet +const ready = ref(false); + +async function refreshNeedsSetup(): Promise { + const res = await fetch("/api/session/needs-setup", { credentials: "same-origin" }); + if (res.ok) { + const body = await res.json(); + needsSetup.value = Boolean(body.needsSetup); + } +} + +async function refreshMe(): Promise { + const res = await fetch("/api/session/me", { credentials: "same-origin" }); + if (res.status === 200) { + currentUser.value = (await res.json()) as User; + } else { + currentUser.value = null; + } +} + +async function refresh(): Promise { + await refreshNeedsSetup(); + if (needsSetup.value) { + currentUser.value = null; + } else { + await refreshMe(); + } + ready.value = true; +} + +async function login(username: string, password: string): Promise { + const res = await fetch("/api/session/login", { + method: "POST", + credentials: "same-origin", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, password }), + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(body.error ?? `login failed (${res.status})`); + } + currentUser.value = (await res.json()) as User; +} + +async function setup(username: string, password: string): Promise { + const res = await fetch("/api/session/setup", { + method: "POST", + credentials: "same-origin", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, password }), + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(body.error ?? `setup failed (${res.status})`); + } + currentUser.value = (await res.json()) as User; + needsSetup.value = false; +} + +async function logout(): Promise { + await fetch("/api/session/logout", { method: "POST", credentials: "same-origin" }); + currentUser.value = null; +} + +export function useSession() { + return { + currentUser: readonly(currentUser), + needsSetup: readonly(needsSetup), + isAuthenticated: computed(() => currentUser.value !== null), + ready: readonly(ready), + refresh, + login, + logout, + setup, + }; +}