owltide/components/Header.vue
Hornwitser 150cb82f5c Basic account and session system
Provide a basic account system with login and server side session store
identified by a cookie.  Upon successful login a signed session cookie
is set by the server with the session stored on the server identifying
which account it is logged in as.  The client uses a shared useFetch on
the session endpoint to identify if it's logged in and which account it
is logged in as, and refreshes this when loggin in or out.
2025-03-07 12:41:57 +01:00

64 lines
1.3 KiB
Vue

<template>
<header>
<nav>
<ul>
<NuxtLink to="/">Home</NuxtLink>
<NuxtLink to="/schedule">Schedule</NuxtLink>
</ul>
</nav>
<div class="account">
<template v-if="session?.account">
{{ session?.account.name || "anonymous" }}
(s:{{ session?.id }} a:{{ session?.account.id }})
{{ session?.account.type }}
<button type="button" @click="logOut">Log out</button>
</template>
<template v-else>
<NuxtLink to="/login">Log In</NuxtLink>
</template>
</div>
</header>
</template>
<script lang="ts" setup>
const { data: session, refresh: sessionRefresh } = useAccountSession();
async function logOut() {
try {
const res = await $fetch.raw("/api/auth/session", {
method: "DELETE",
});
await sessionRefresh();
} catch (err: any) {
alert(`Log out failed: ${err.statusCode} ${err.statusMessage}`);
}
}
</script>
<style scoped>
header {
line-height: 1.5; /* Prevent layout shift from log out button */
display: flex;
column-gap: 1em;
flex-wrap: wrap;
border-bottom: 1px solid var(--foreground);
margin-block-start: 1rem;
}
.account {
display: flex;
justify-content: end;
flex-grow: 1;
column-gap: 0.5em;
flex-wrap: wrap;
}
nav ul {
padding: 0;
display: flex;
column-gap: 0.5em;
}
button {
font-family: inherit;
font-size: inherit;
}
</style>