Quellcode durchsuchen

Add token renewal functionality: implement renewalToken method in userApi, update useAuth composable to handle token refresh, and modify login/logout components to utilize new userProfile state. Remove unused user composable and auth middleware.

0es vor 4 Monaten
Ursprung
Commit
917aec09d8

+ 9 - 0
app/api/user.ts

@@ -7,6 +7,7 @@ import type {
   UserInfoVO,
   UserLoginVO,
   UserThirdLoginDTO,
+  TokenRenewalVO,
 } from '~/types/api'
 
 /**
@@ -37,6 +38,14 @@ export const userApi = {
   logout() {
     return http.post<object>('/user/logout')
   },
+
+  /**
+   * Renew token - refresh authentication token
+   * @returns Token renewal response
+   */
+  renewalToken() {
+    return http.post<TokenRenewalVO>('/user/renewalToken')
+  },
 }
 
 /**

+ 43 - 13
app/composables/useAuth.ts

@@ -1,26 +1,54 @@
 import { storeToRefs } from 'pinia'
 import { useAuthStore } from '~/stores/auth'
+import { userApi } from '~/api/user'
 
 // Authentication composable for managing user auth state via Pinia
 export const useAuth = () => {
   const authStore = useAuthStore()
-  const { isAuthenticated, token } = storeToRefs(authStore)
-
-  const setAuth = (value: string) => {
-    authStore.setAuth(value)
-  }
+  const { isAuthenticated, token, userProfile } = storeToRefs(authStore)
 
   const restoreAuth = () => {
     authStore.restoreAuth()
   }
 
-  const getToken = (): string | null => {
-    return token.value
+  const refreshAuth = async () => {
+    try {
+      await userApi.renewalToken()
+    }
+    catch (error) {
+      console.error('Failed to refresh token:', error)
+      throw error
+    }
+  }
+
+  const refreshUser = async () => {
+    try {
+      const result = await userApi.getMyInfo()
+
+      authStore.setUserProfile(result.userProfile)
+    }
+    catch (error) {
+      console.error('Failed to fetch user:', error)
+    }
   }
 
-  // Get user info (currently just returns token)
-  const getUser = (): string | null => {
-    return token.value
+  const login = async (type: 'google', googleData: string) => {
+    try {
+      const result = await userApi.loginWithGoogle({ data: googleData })
+
+      if (result) {
+        authStore.setAuth(result.token)
+        authStore.setUserProfile(result.userProfile)
+
+        return result
+      }
+
+      return null
+    }
+    catch (error) {
+      console.error('Failed to login:', error)
+      return null
+    }
   }
 
   const logout = () => {
@@ -29,10 +57,12 @@ export const useAuth = () => {
 
   return {
     isAuthenticated,
-    setAuth,
+    token,
+    userProfile,
     restoreAuth,
-    getToken,
-    getUser,
+    refreshAuth,
+    refreshUser,
+    login,
     logout,
   }
 }

+ 0 - 108
app/composables/useUser.ts

@@ -1,108 +0,0 @@
-/**
- * User composable - provides user-related functionality
- */
-
-import { userApi } from '~/api/user'
-import type { UserInfoVO } from '~/types/api'
-
-export const useUser = () => {
-  const userInfo = ref<UserInfoVO | null>(null)
-  const loading = ref(false)
-  const error = ref<string | null>(null)
-
-  /**
-   * Fetch current user info
-   */
-  const fetchUserInfo = async () => {
-    loading.value = true
-    error.value = null
-
-    try {
-      const result = await userApi.getMyInfo()
-      userInfo.value = result
-      return result
-    }
-    catch (err) {
-      const message = err instanceof Error ? err.message : 'Failed to fetch user info'
-      error.value = message
-      console.error('fetchUserInfo error:', err)
-      return null
-    }
-    finally {
-      loading.value = false
-    }
-  }
-
-  /**
-   * Google login
-   */
-  const loginWithGoogle = async (googleData: string) => {
-    loading.value = true
-    error.value = null
-
-    try {
-      const result = await userApi.loginWithGoogle({ data: googleData })
-
-      if (result) {
-        // Save token using useAuth
-        const { setAuth } = useAuth()
-        setAuth(result.token)
-
-        // Update user info
-        userInfo.value = {
-          userProfile: result.userProfile,
-        }
-
-        return result
-      }
-
-      return null
-    }
-    catch (err) {
-      const message = err instanceof Error ? err.message : 'Login failed'
-      error.value = message
-      console.error('loginWithGoogle error:', err)
-      return null
-    }
-    finally {
-      loading.value = false
-    }
-  }
-
-  /**
-   * Logout user
-   */
-  const logoutUser = async () => {
-    loading.value = true
-    error.value = null
-
-    try {
-      await userApi.logout()
-
-      // Clear token and user info
-      const { logout: clearAuth } = useAuth()
-      clearAuth()
-      userInfo.value = null
-
-      return true
-    }
-    catch (err) {
-      const message = err instanceof Error ? err.message : 'Logout failed'
-      error.value = message
-      console.error('logoutUser error:', err)
-      return false
-    }
-    finally {
-      loading.value = false
-    }
-  }
-
-  return {
-    userInfo: readonly(userInfo),
-    loading: readonly(loading),
-    error: readonly(error),
-    fetchUserInfo,
-    loginWithGoogle,
-    logoutUser,
-  }
-}

+ 0 - 0
app/middleware/auth.ts → app/middleware/auth.global.ts


+ 5 - 10
app/pages/index.vue

@@ -1,18 +1,12 @@
 <script setup lang="ts">
 // This page requires authentication
 definePageMeta({
-  middleware: 'auth',
+  auth: false,
 })
 
-const { logout, getUser } = useAuth()
+const { logout, userProfile, isAuthenticated } = useAuth()
 const router = useRouter()
 
-const user = ref<string | null>(null)
-
-onMounted(() => {
-  user.value = getUser()
-})
-
 const handleLogout = () => {
   logout()
   router.push('/login')
@@ -27,6 +21,7 @@ const handleLogout = () => {
           欢迎来到 Lanu
         </h1>
         <button
+          v-if="isAuthenticated"
           class="logout-button"
           @click="handleLogout"
         >
@@ -50,12 +45,12 @@ const handleLogout = () => {
         <div class="info-section">
           <h3>用户信息</h3>
           <div
-            v-if="user"
+            v-if="isAuthenticated"
             class="user-info"
           >
             <p>已登录状态</p>
             <p class="user-token">
-              Token: {{ user.substring(0, 30) }}...
+              {{ userProfile }}
             </p>
           </div>
         </div>

+ 7 - 11
app/pages/login.vue

@@ -11,23 +11,19 @@ definePageMeta({
 })
 
 const router = useRouter()
-const { setAuth, isAuthenticated } = useAuth()
-const { loginWithGoogle } = useUser()
+const { login, isAuthenticated } = useAuth()
 
 onMounted(() => {
-  if (isAuthenticated.value) {
-    router.push('/')
-  }
+  watch(isAuthenticated, (newIsAuthenticated) => {
+    if (newIsAuthenticated) {
+      router.push('/')
+    }
+  })
 })
 
 // Google SignIn
 const handleLoginSuccess = async (response: CredentialResponse) => {
-  const result = await loginWithGoogle(response.credential || '')
-
-  if (result) {
-    setAuth(result.token)
-    router.push('/')
-  }
+  await login('google', response.credential || '')
 }
 
 const handleLoginError = () => {

+ 13 - 10
app/plugins/auth.client.ts

@@ -1,16 +1,19 @@
 export default defineNuxtPlugin({
   name: 'auth',
-  async setup(_nuxtApp) {
-    const { isAuthenticated, restoreAuth } = useAuth()
+  async setup(nuxtApp) {
+    const { isAuthenticated, restoreAuth, refreshAuth, refreshUser } = useAuth()
 
-    watch(isAuthenticated, (newIsAuthenticated) => {
-      if (newIsAuthenticated) {
-        console.log('User is authenticated')
-      }
-    }, {
-      immediate: false,
-    })
+    nuxtApp.hook('app:mounted', () => {
+      watch(isAuthenticated, (newIsAuthenticated) => {
+        if (newIsAuthenticated) {
+          refreshAuth()
+          refreshUser()
+        }
+      }, {
+        immediate: false,
+      })
 
-    restoreAuth()
+      restoreAuth()
+    })
   },
 })

+ 5 - 0
app/stores/auth.ts

@@ -1,9 +1,11 @@
 import { defineStore } from 'pinia'
 import { TOKEN_KEY } from '~/constants'
+import type { UserProfileVO } from '~/types/api'
 
 export const useAuthStore = defineStore('auth', {
   state: () => ({
     token: null as string | null,
+    userProfile: null as UserProfileVO | null,
   }),
   getters: {
     isAuthenticated: state => !!state.token,
@@ -22,6 +24,9 @@ export const useAuthStore = defineStore('auth', {
         localStorage.setItem(TOKEN_KEY, token)
       }
     },
+    setUserProfile(userProfile: UserProfileVO) {
+      this.userProfile = userProfile
+    },
     // 退出登录
     logout() {
       this.token = null

+ 6 - 0
app/types/api/user.ts

@@ -26,3 +26,9 @@ export interface UserLoginVO {
 export interface UserThirdLoginDTO {
   data: string
 }
+
+// Token Renewal Response
+export interface TokenRenewalVO {
+  token?: string
+  [key: string]: unknown
+}

+ 5 - 5
app/utils/request.ts

@@ -79,7 +79,7 @@ const generateSign = (id: string, time: number, token: string, secret: string, b
  */
 export const createRequest = () => {
   const runtimeConfig = useRuntimeConfig()
-  const { getToken, logout } = useAuth()
+  const { token, logout } = useAuth()
 
   // API base configuration (read within a valid Nuxt context)
   const API_CONFIG = {
@@ -120,7 +120,7 @@ export const createRequest = () => {
     // Generate request-specific values
     const requestId = generateRequestId()
     const time = Date.now()
-    const token = getToken() || ''
+    const tokenVal = token.value || ''
 
     // Add custom API headers
     headers['id'] = requestId
@@ -135,13 +135,13 @@ export const createRequest = () => {
     headers['time'] = String(time)
 
     // Add token if exists
-    if (token) {
-      headers['token'] = token
+    if (tokenVal) {
+      headers['token'] = tokenVal
     }
 
     // Generate and add signature
     const body = options.body
-    const sign = generateSign(requestId, time, token, API_CONFIG.secret, body)
+    const sign = generateSign(requestId, time, tokenVal, API_CONFIG.secret, body)
     headers['sign'] = sign
 
     // Add default content type if not already set