From d1dd2c00f6b89b110f70e235dfcae53262cccebe Mon Sep 17 00:00:00 2001 From: AfonsoCMSousa Date: Thu, 18 Dec 2025 21:04:49 +0000 Subject: [PATCH] SyntaxSquad/DADProject#9 feat: User info dysplayed in /users/ --- frontend/src/components/layout/NavBar.vue | 3 + frontend/src/main.js | 4 + frontend/src/pages/login/LoginPage.vue | 111 +++++++-------- frontend/src/pages/user/UserPage.vue | 159 +++++++++++++++++++--- frontend/src/stores/api.js | 12 ++ frontend/src/stores/auth.js | 62 +++++++-- frontend/src/stores/user.js | 47 +++++-- 7 files changed, 305 insertions(+), 93 deletions(-) diff --git a/frontend/src/components/layout/NavBar.vue b/frontend/src/components/layout/NavBar.vue index cc66d7a..533d0c4 100644 --- a/frontend/src/components/layout/NavBar.vue +++ b/frontend/src/components/layout/NavBar.vue @@ -24,6 +24,9 @@ Logout + + Profile + diff --git a/frontend/src/main.js b/frontend/src/main.js index 27177c5..a14e65a 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,6 +1,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import { io } from 'socket.io-client' +import { useAuthStore } from '@/stores/auth' import App from './App.vue' import router from './router' @@ -20,4 +21,7 @@ app.provide('apiBaseURL', `http://${apiDomain}/api`) app.use(createPinia()) app.use(router) +const authStore = useAuthStore() +authStore.initAuth() + app.mount('#app') diff --git a/frontend/src/pages/login/LoginPage.vue b/frontend/src/pages/login/LoginPage.vue index 7ddc2e5..e1afd46 100644 --- a/frontend/src/pages/login/LoginPage.vue +++ b/frontend/src/pages/login/LoginPage.vue @@ -1,55 +1,55 @@ \ No newline at end of file + diff --git a/frontend/src/pages/user/UserPage.vue b/frontend/src/pages/user/UserPage.vue index e952503..41570ef 100644 --- a/frontend/src/pages/user/UserPage.vue +++ b/frontend/src/pages/user/UserPage.vue @@ -2,36 +2,163 @@

My Profile

-
-

Name: {{ userStore.user.name }}

-

Email: {{ userStore.user.email }}

-

Joined: {{ new Date(userStore.user.created_at).toLocaleString() }}

+ +
+

Loading your profile...

-
-

Loading... 読み込み中

+ +
+

Error: {{ error }}

+ +
+ + + + + +
+

No user data or not logged in. Please log in to view your profile.

- - diff --git a/frontend/src/stores/api.js b/frontend/src/stores/api.js index 3920e25..8ff3ba8 100644 --- a/frontend/src/stores/api.js +++ b/frontend/src/stores/api.js @@ -1,3 +1,4 @@ +// src/stores/api.js import { defineStore } from 'pinia' import axios from 'axios' import { inject, ref } from 'vue' @@ -21,7 +22,10 @@ export const useAPIStore = defineStore('api', () => { const response = await axios.post(`${API_BASE_URL}/login`, credentials) token.value = response.data.token axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}` + + return response } + const postLogout = async () => { await axios.post(`${API_BASE_URL}/logout`) token.value = undefined @@ -53,11 +57,19 @@ export const useAPIStore = defineStore('api', () => { return axios.get(`${API_BASE_URL}/games?${queryParams}`) } + const initToken = (storedToken) => { + if (storedToken) { + token.value = storedToken + axios.defaults.headers.common['Authorization'] = `Bearer ${storedToken}` + } + } + return { postLogin, postLogout, getAuthUser, getGames, gameQueryParameters, + initToken, } }) diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index c5bef31..1cec1b3 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -1,6 +1,8 @@ +// src/stores/auth.js import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { useAPIStore } from './api' +import axios from 'axios' export const useAuthStore = defineStore('auth', () => { const apiStore = useAPIStore() @@ -20,8 +22,10 @@ export const useAuthStore = defineStore('auth', () => { token.value = jwtToken if (jwtToken) { localStorage.setItem('token', jwtToken) + axios.defaults.headers.common['Authorization'] = `Bearer ${jwtToken}` } else { localStorage.removeItem('token') + delete axios.defaults.headers.common['Authorization'] } } @@ -30,20 +34,57 @@ export const useAuthStore = defineStore('auth', () => { } const login = async (credentials) => { - await apiStore.postLogin(credentials) - const response = await apiStore.getAuthUser() - const jwtToken = response - const user = response.data + try { + // postLogin already sets the token and axios headers, and returns the response + const loginResponse = await apiStore.postLogin(credentials) - setToken(jwtToken) - currentUser.value = response.data - return response.data + // Extract the token from the response + const jwtToken = loginResponse.data.token + + // Store token in auth store (for localStorage) + setToken(jwtToken) + + // Use the user data from login response (it's already there!) + currentUser.value = loginResponse.data.user + + return currentUser.value + } catch (error) { + // Clear everything on login failure + setToken(null) + currentUser.value = undefined + throw error + } } const logout = async () => { - await apiStore.postLogout() - currentUser.value = undefined - setToken(null) + try { + await apiStore.postLogout() + } catch (error) { + console.error('Logout API call failed:', error) + } finally { + currentUser.value = undefined + setToken(null) + } + } + + // Initialize auth from localStorage on app start + const initAuth = async () => { + if (token.value) { + axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}` + apiStore.initToken(token.value) + + // Try to fetch current user if we don't have it + if (!currentUser.value) { + try { + const userResponse = await apiStore.getAuthUser() + currentUser.value = userResponse.data + } catch (error) { + console.error('Failed to restore user session:', error) + // Clear invalid token + setToken(null) + } + } + } } return { @@ -55,5 +96,6 @@ export const useAuthStore = defineStore('auth', () => { getToken, login, logout, + initAuth, } }) diff --git a/frontend/src/stores/user.js b/frontend/src/stores/user.js index 2a678ea..ea53013 100644 --- a/frontend/src/stores/user.js +++ b/frontend/src/stores/user.js @@ -1,39 +1,58 @@ import { defineStore } from 'pinia' import axios from 'axios' +import { useAuthStore } from './auth' export const useUserStore = defineStore('user', { state: () => ({ user: null, - token: localStorage.getItem('token') || null, + loading: false, + error: null }), + getters: { + currentUser: (state) => state.user + }, + actions: { - async fetchUser() { - if (!this.token) return + async fetchUserProfile() { + const authStore = useAuthStore() + const token = authStore.getToken() + + if (!token) { + this.error = 'No authentication token found' + throw new Error('No authentication token found') + } + + this.loading = true + this.error = null try { const response = await axios.get('/api/users/me', { headers: { - Authorization: `Bearer ${this.token}` + Authorization: `Bearer ${token}`, + Accept: 'application/json' } }) this.user = response.data + return response.data } catch (error) { - console.error("Failed to fetch user") + this.error = error.response?.data?.message || 'Failed to fetch user profile' + + if (error.response?.status === 401) { + authStore.logout() + } + + throw error + } finally { + this.loading = false } }, - setToken(token) { - this.token = token - localStorage.setItem('token', token) - }, - - logout() { - this.token = null + clearUser() { this.user = null - localStorage.removeItem('token') + this.error = null + this.loading = false } } }) -