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}
+