From 19aca0e5fc6a03ddaf6e3636b9587a380142d38b Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 20 Mar 2026 01:11:20 +0100 Subject: [PATCH] feat: auth store, router guard and login page --- src/AccountTracking.Web/src/router/index.ts | 10 ++- src/AccountTracking.Web/src/stores/auth.ts | 30 +++++++++ .../src/views/LoginView.vue | 66 ++++++++++++++++++- 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/AccountTracking.Web/src/stores/auth.ts diff --git a/src/AccountTracking.Web/src/router/index.ts b/src/AccountTracking.Web/src/router/index.ts index 716e898..1316f6a 100644 --- a/src/AccountTracking.Web/src/router/index.ts +++ b/src/AccountTracking.Web/src/router/index.ts @@ -1,12 +1,20 @@ import { createRouter, createWebHistory } from 'vue-router' +import { useAuthStore } from '../stores/auth' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', component: () => import('../views/LoginView.vue'), meta: { public: true } }, - { path: '/', component: () => import('../views/DashboardView.vue') }, + { path: '/', component: () => import('../views/DashboardView.vue') }, { path: '/transactions', component: () => import('../views/TransactionsView.vue') }, ], }) +router.beforeEach((to) => { + if (to.meta.public) return true + const auth = useAuthStore() + if (!auth.isAuthenticated) return '/login' + return true +}) + export default router diff --git a/src/AccountTracking.Web/src/stores/auth.ts b/src/AccountTracking.Web/src/stores/auth.ts new file mode 100644 index 0000000..473ebf2 --- /dev/null +++ b/src/AccountTracking.Web/src/stores/auth.ts @@ -0,0 +1,30 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { authApi } from '../api' + +export const useAuthStore = defineStore('auth', () => { + const token = ref(localStorage.getItem('token')) + const expiresAt = ref(localStorage.getItem('expiresAt')) + + const isAuthenticated = computed(() => { + if (!token.value || !expiresAt.value) return false + return Date.now() < Date.parse(expiresAt.value) + }) + + async function login(username: string, password: string): Promise { + const response = await authApi.login({ username, password }) + token.value = response.token ?? null + expiresAt.value = response.expiresAt ?? null + if (token.value) localStorage.setItem('token', token.value) + if (expiresAt.value) localStorage.setItem('expiresAt', expiresAt.value) + } + + function logout() { + token.value = null + expiresAt.value = null + localStorage.removeItem('token') + localStorage.removeItem('expiresAt') + } + + return { token, isAuthenticated, login, logout } +}) diff --git a/src/AccountTracking.Web/src/views/LoginView.vue b/src/AccountTracking.Web/src/views/LoginView.vue index d6f47ba..4a15ff4 100644 --- a/src/AccountTracking.Web/src/views/LoginView.vue +++ b/src/AccountTracking.Web/src/views/LoginView.vue @@ -1 +1,65 @@ - + + +