diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..8ca546d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 03d9549..9c69411 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,5 +2,6 @@ \ No newline at end of file diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..ca80e87 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -2,11 +2,20 @@ // for information about these interfaces declare global { namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} + interface User { + id: string; + username: string; + nickname: string; + avatar?: string; + roles: string[]; + } + interface Locals { + user: User | null; + } + + interface pageData { + user: User | null; + } } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..3f42a17 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,30 @@ +import type { Handle } from '@sveltejs/kit'; +import { parseJwt } from '$lib/utils/tokenUtils.ts'; +import type { JwtPayload } from '$lib/types/auth.ts'; +import { COOKIE_TOKEN_KEY } from '$lib/components/constants/cookiesConstants.ts'; + +export const handle: Handle = async ({ event, resolve}) =>{ + const authorization = event.cookies.get(COOKIE_TOKEN_KEY); + if (authorization){ + const split = authorization?.split(' '); + + const token = split[1]; + + + const jwt = parseJwt(token); + + if (jwt){ + event.locals.user = { + id: jwt.userId, + username: jwt.sub, + nickname: jwt.nickname, + avatar: jwt.avatar, + roles: jwt.authorities + } + } + } + + return resolve(event); + +} + diff --git a/src/lib/api/httpClient.ts b/src/lib/api/httpClient.ts index 58b7aa3..97db7b9 100644 --- a/src/lib/api/httpClient.ts +++ b/src/lib/api/httpClient.ts @@ -1,8 +1,6 @@ // src/lib/api/httpClient.ts -import { browser } from '$app/environment'; import type { HttpMethod, JsonObject, JsonValue } from '$lib/types/http.ts'; -import { authStore } from '$lib/stores/authStore.ts'; import type { ApiResult } from '$lib/types/api.ts'; @@ -11,16 +9,8 @@ interface RequestOptions extends Omit { } const API_BASE_URL = import.meta.env.VITE_PUBLIC_API_URL || 'http://localhost:18888/api'; -let currentToken: string | null = null; -let currentTokenHead: string | null = null; -if (browser) { - // 只有在浏览器环境下才订阅,防止 SSR 内存泄漏 - authStore.subscribe(state => { - currentToken = state.token; - currentTokenHead = state.tokenHead; - }); -} + const normalizeHeaders = (headers?: HeadersInit):Record =>{ const result:Record = {}; @@ -74,8 +64,8 @@ const httpRequest = async ( const canHaveBody = method !== 'GET' ; - // 【修改点 2】:只有在允许携带 Body 时才处理 if (canHaveBody) { + console.log('body', body); if (body instanceof FormData) { requestBody = body; } else if (body) { @@ -85,9 +75,9 @@ const httpRequest = async ( } // ... Token 处理逻辑保持不变 ... - if (currentToken && currentTokenHead) { - requestHeaders['authorization'] = `${currentTokenHead} ${currentToken}`; - } + // if (currentToken && currentTokenHead) { + // requestHeaders['authorization'] = `${currentTokenHead} ${currentToken}`; + // } try { const response = await fetch(fullUrl, { diff --git a/src/lib/api/services/authService.ts b/src/lib/api/services/authService.ts index c060a4d..3b73830 100644 --- a/src/lib/api/services/authService.ts +++ b/src/lib/api/services/authService.ts @@ -1,8 +1,7 @@ import { api } from '$lib/api/httpClient'; // 通常不需要 .ts 后缀 import type { AuthResponse, LoginPayload } from '$lib/types/auth'; -import { authStore } from '$lib/stores/authStore'; -import { toast } from '$lib/stores/toastStore'; -import { ResponseError } from '$lib/types/error.ts'; +import { ApiError } from '$lib/types/api.ts'; + export const authService = { /** @@ -13,23 +12,22 @@ export const authService = { const response = await api.post('/auth/login', payload); if (response.code !== 200 || !response.data) { - throw new ResponseError(response); + throw new ApiError(response); } - const { token, tokenHead,userProfile } = response.data; - - authStore.update(s => ({ ...s, token, tokenHead, isAuthenticated: true,user: userProfile })); - - return response.data; - }, /** * 登出流程 */ logout: async () => { - authStore.logout(); - toast.success('退出登录成功'); + try { + // Optionally call the backend logout endpoint + await api.post('/auth/logout', {}); + } catch (error) { + // Even if the backend call fails, we still want to clear local state + console.warn('Logout API call failed:', error); + } } }; \ No newline at end of file diff --git a/src/lib/api/services/tokenService.ts b/src/lib/api/services/tokenService.ts new file mode 100644 index 0000000..c5fe7be --- /dev/null +++ b/src/lib/api/services/tokenService.ts @@ -0,0 +1,45 @@ +import { api } from '$lib/api/httpClient'; +import type { ApiResult } from '$lib/types/api'; +import { authStore } from '$lib/stores/authStore'; +import { browser } from '$app/environment'; + +export const tokenService = { + /** + * Check if the current token is valid + */ + validateToken: async (): Promise => { + if (!browser) return false; + + try { + const response = await api.get('/auth/validate'); + return response.code === 200; + } catch (error) { + console.error('Token validation failed:', error); + return false; + } + }, + + /** + * Refresh the current token + */ + refreshToken: async (): Promise => { + if (!browser) return false; + + try { + const response = await api.post<{token: string, tokenHead: string}>('/auth/refresh', {}); + if (response.code === 200 && response.data) { + // Update the auth store with new token + authStore.update(state => ({ + ...state, + token: response.data!.token, + tokenHead: response.data!.tokenHead + })); + return true; + } + return false; + } catch (error) { + console.error('Token refresh failed:', error); + return false; + } + } +}; \ No newline at end of file diff --git a/src/lib/assets/sprite.svg b/src/lib/assets/sprite.svg deleted file mode 100644 index 139597f..0000000 --- a/src/lib/assets/sprite.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/lib/components/ToastContainer.svelte b/src/lib/components/ToastContainer.svelte index 4b07cea..afdb449 100644 --- a/src/lib/components/ToastContainer.svelte +++ b/src/lib/components/ToastContainer.svelte @@ -1,9 +1,11 @@
- {#each $toast as t (t.id)} + {#each toastState.toasts as t (t.id)}
+ + \ No newline at end of file diff --git a/src/lib/components/layout/app/AppHeader.svelte b/src/lib/components/layout/app/AppHeader.svelte index 9502158..7a94d5b 100644 --- a/src/lib/components/layout/app/AppHeader.svelte +++ b/src/lib/components/layout/app/AppHeader.svelte @@ -1,18 +1,16 @@
- {/if} + {/if}
\ No newline at end of file diff --git a/src/lib/components/layout/app/AppSidebar.svelte b/src/lib/components/layout/app/AppSidebar.svelte index a53ff79..1788e63 100644 --- a/src/lib/components/layout/app/AppSidebar.svelte +++ b/src/lib/components/layout/app/AppSidebar.svelte @@ -2,12 +2,11 @@ import { resolve } from '$app/paths'; import { page } from '$app/state'; import { fly, fade } from 'svelte/transition'; - import Icon from '$lib/components/icon/Icon.svelte'; - import { sidebarStore, setSidebarOpen } from '$lib/stores/sidebarStore'; - import { authStore } from '$lib/stores/authStore'; import type { NavItem, ProcessedNavItem } from '$lib/types/layout'; - import { authService } from '$lib/api/services/authService'; + + + // 1. 模拟数据:包含三层结构 @@ -105,12 +104,7 @@ // 使用 $derived 动态计算,类型自动推断为 ProcessedNavItem[] let navItems = $derived(processNavItems(rawNavItems, page.url.pathname)); - function handleMobileClose() { - const isMobile = window.innerWidth < 768; - if (isMobile && $sidebarStore.isOpen) { - setSidebarOpen(false); - } - } + @@ -134,7 +128,6 @@ {:else} {#if item.icon} @@ -149,13 +142,12 @@ {/snippet} -{#if $sidebarStore.isOpen} +
e.key === 'Escape' && handleMobileClose()} + transition:fade={{ duration: 200 }} >
@@ -168,9 +160,8 @@
- @@ -184,7 +175,8 @@ - {#if $authStore.isAuthenticated && $authStore.user} + + {#if page.data.user}
- {/if} + {/if} -{/if}