feat(auth): implement user logout functionality

- Add logout action to delete auth cookie and reset user state
- Create logout form with enhanced submit handling
- Integrate toast notifications for logout feedback
- Update sidebar to include logout button with form submission
- Remove unnecessary console log in layout server load
- Clean up JWT parsing logic in hooks server file
- Add new LogoutButton component (currently empty)
This commit is contained in:
Chaos
2025-11-25 17:23:43 +08:00
parent 4ec8e88e58
commit 81c61f433d
5 changed files with 61 additions and 7 deletions

View File

@@ -7,10 +7,7 @@ export const handle: Handle = async ({ event, resolve}) =>{
const authorization = event.cookies.get(COOKIE_TOKEN_KEY); const authorization = event.cookies.get(COOKIE_TOKEN_KEY);
if (authorization){ if (authorization){
const split = authorization?.split(' '); const split = authorization?.split(' ');
const token = split[1]; const token = split[1];
const jwt = parseJwt<JwtPayload>(token); const jwt = parseJwt<JwtPayload>(token);
if (jwt){ if (jwt){

View File

@@ -0,0 +1,8 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { getContext } from 'svelte';
import { TOAST_KEY, type ToastState } from '$lib/stores/toast.svelte.ts';
import Icon from '$lib/components/icon/Icon.svelte'; // 假设你的路径
</script>

View File

@@ -4,7 +4,9 @@
import { fly, fade } from 'svelte/transition'; import { fly, fade } from 'svelte/transition';
import Icon from '$lib/components/icon/Icon.svelte'; import Icon from '$lib/components/icon/Icon.svelte';
import type { NavItem, ProcessedNavItem } from '$lib/types/layout'; import type { NavItem, ProcessedNavItem } from '$lib/types/layout';
import { getContext } from 'svelte';
import { TOAST_KEY, type ToastState } from '$lib/stores/toast.svelte.ts';
import { enhance } from '$app/forms';
@@ -104,7 +106,26 @@
// 使用 $derived 动态计算,类型自动推断为 ProcessedNavItem[] // 使用 $derived 动态计算,类型自动推断为 ProcessedNavItem[]
let navItems = $derived(processNavItems(rawNavItems, page.url.pathname)); let navItems = $derived(processNavItems(rawNavItems, page.url.pathname));
// 获取 Toast 以便提示用户
const toast = getContext<ToastState>(TOAST_KEY);
// 处理提交结果的回调
const handleLogout = () => {
toast.info('正在退出登录...');
return async ({ result, update }) => {
// result.type 可能是 'redirect', 'success', 'failure'
if (result.type === 'redirect') {
toast.success('您已安全退出');
}
// update() 会触发默认行为(也就是执行 redirect 跳转)
await update();
};
};
let logoutForm: HTMLFormElement;
</script> </script>
<!-- 定义递归 Snippet显式指定类型 --> <!-- 定义递归 Snippet显式指定类型 -->
@@ -213,11 +234,23 @@
<a href="/app/settings"><Icon id="settings" size="16" /> 设置</a> <a href="/app/settings"><Icon id="settings" size="16" /> 设置</a>
</li> </li>
<div class="divider my-1"></div> <div class="divider my-1"></div>
<li>
<button class="text-error" > <li class="">
<button
class="text-error w-full text-left flex items-center gap-2"
on:click={() => logoutForm.requestSubmit()}
>
<Icon id="sign-out" size="16" /> 退出登录 <Icon id="sign-out" size="16" /> 退出登录
</button> </button>
</li> </li>
<form
action="/auth/logout"
method="POST"
use:enhance={handleLogout}
bind:this={logoutForm}
hidden
>
</form>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -7,7 +7,6 @@ import { COOKIE_THEME_KEY } from '$lib/components/constants/cookiesConstants.ts'
export const load: LayoutServerLoad = async ({url,cookies,locals}) => { export const load: LayoutServerLoad = async ({url,cookies,locals}) => {
console.log("locals",locals);
const targetPath: RouteId = '/app/dashboard'; const targetPath: RouteId = '/app/dashboard';
if (url.pathname === '/') { if (url.pathname === '/') {

View File

@@ -0,0 +1,17 @@
import type { Actions } from './$types';
import { redirect } from '@sveltejs/kit';
import { resolve } from '$app/paths';
import { COOKIE_TOKEN_KEY } from '$lib/components/constants/cookiesConstants.ts';
export const actions:Actions = {
default: async ({ cookies,locals}) => {
cookies.delete(COOKIE_TOKEN_KEY,{path:'/'});
locals.user = null;
throw redirect(302, resolve('/'));
}
}