feat(web): implement initial logic to handle protected vs public routing
This commit is contained in:
Vendored
+1
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"dotnet.defaultSolution": "FiscalOS.slnx",
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"tsconfig.json": "tsconfig.*.json, env.d.ts, typed-router.d.ts",
|
||||
|
||||
Generated
+10
@@ -8,6 +8,7 @@
|
||||
"name": "fiscalos-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"jwt-decode": "^4.0.0",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "beta",
|
||||
"vue-router": "^5.0.2"
|
||||
@@ -5145,6 +5146,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"format": "prettier --write --experimental-cli src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"jwt-decode": "^4.0.0",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "beta",
|
||||
"vue-router": "^5.0.2"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router';
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>FiscalOS</h1>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,8 +1,49 @@
|
||||
import { useUserStore } from "@/stores/userStore";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [],
|
||||
routes: [
|
||||
{
|
||||
path: '/public',
|
||||
redirect: '/public/login',
|
||||
beforeEnter: () => {
|
||||
const userStore = useUserStore();
|
||||
|
||||
if (userStore.user) {
|
||||
return { path: '/' };
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
component: () => import('../views/LoginView.vue')
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
beforeEnter: () => {
|
||||
// TODO: Handle expired token
|
||||
// Try to refresh
|
||||
// if refresh fails log out
|
||||
// redirect to login page
|
||||
// if refresh succeeds
|
||||
// log user in and allow
|
||||
// to pass
|
||||
const userStore = useUserStore();
|
||||
|
||||
if (userStore.user === null) {
|
||||
return { path: '/public/login' }
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
component: () => import('../views/HomeView.vue'),
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const USER_KEY = 'onxGraphAuth';
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
expiresAtInSeconds: number;
|
||||
token: string;
|
||||
};
|
||||
|
||||
type JwtTokenPayload = {
|
||||
sub: string;
|
||||
exp: number;
|
||||
};
|
||||
|
||||
function getUserFromLocalStorage(): User | null {
|
||||
const user = localStorage.getItem(USER_KEY);
|
||||
return user === null ? null : JSON.parse(user);
|
||||
}
|
||||
|
||||
export type UserStore = ReturnType<typeof useUserStore>;
|
||||
|
||||
export const useUserStore = defineStore('userStore', () => {
|
||||
const user = ref<User | null>(getUserFromLocalStorage());
|
||||
|
||||
function logUserIn(jwtToken: string) {
|
||||
const { sub, exp } = jwtDecode<JwtTokenPayload>(jwtToken);
|
||||
const loggedInUser: User = {
|
||||
id: sub,
|
||||
expiresAtInSeconds: exp,
|
||||
token: jwtToken,
|
||||
};
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(loggedInUser));
|
||||
user.value = loggedInUser;
|
||||
}
|
||||
|
||||
function logUserOut() {
|
||||
localStorage.removeItem(USER_KEY);
|
||||
user.value = null;
|
||||
}
|
||||
|
||||
return {
|
||||
user: user,
|
||||
logUserIn,
|
||||
logUserOut,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<h1>Home View</h1>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { logUserIn } = useUserStore();
|
||||
const router = useRouter();
|
||||
|
||||
async function handleSubmit(e: SubmitEvent) {
|
||||
const data = Object.fromEntries(new FormData(e.currentTarget as HTMLFormElement));
|
||||
const response = await fetch('/api/auth/login', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok === false) {
|
||||
alert('Failed to login');
|
||||
return;
|
||||
}
|
||||
|
||||
const responseBody = await response.json();
|
||||
|
||||
logUserIn(responseBody.accessToken)
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Login View</h1>
|
||||
<form v-on:submit.prevent="handleSubmit">
|
||||
<div>
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" id="username">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -7,6 +7,7 @@ import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
import mkcert from 'vite-plugin-mkcert';
|
||||
|
||||
export default defineConfig({
|
||||
publicDir: 'static',
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
|
||||
Reference in New Issue
Block a user