From d296b7aa7233a9182d41dbf43021da3ec643a895 Mon Sep 17 00:00:00 2001 From: flym Date: Sat, 12 Sep 2026 03:25:46 +0800 Subject: [PATCH] feat: header reflects auth state (account/logout); fix admin api requests losing Content-Type (user delete 422) --- components/site-header.tsx | 58 +++++++++++++++++++++++++++++++++----- lib/admin.ts | 13 +++++---- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/components/site-header.tsx b/components/site-header.tsx index 048f9cb..d6798c3 100644 --- a/components/site-header.tsx +++ b/components/site-header.tsx @@ -1,14 +1,32 @@ "use client"; import Link from "next/link"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { CloseIcon, MenuIcon } from "@/components/icons"; import { navigation } from "@/lib/content"; +import { getMe, logout } from "@/lib/admin"; import { BrandMark } from "@/components/brand-mark"; import { BrandIcon } from "@/components/brand-icon"; export function SiteHeader() { const [open, setOpen] = useState(false); + const [authed, setAuthed] = useState(false); + + useEffect(() => { + getMe() + .then(() => setAuthed(true)) + .catch(() => setAuthed(false)); + }, []); + + async function handleLogout() { + try { + await logout(); + setAuthed(false); + setOpen(false); + } catch { + window.location.href = "/login"; + } + } return (
@@ -24,9 +42,20 @@ export function SiteHeader() { ))}
- - 登录 - + {authed ? ( + <> + + 我的页面 + + + + ) : ( + + 登录 + + )} 联系我们 @@ -43,9 +72,24 @@ export function SiteHeader() { {item.label} ))} - setOpen(false)}> - 登录 / 账户入口 - + {authed ? ( + <> + setOpen(false)}> + 我的页面 + + + + ) : ( + setOpen(false)}> + 登录 / 账户入口 + + )}
) : null} diff --git a/lib/admin.ts b/lib/admin.ts index be654a2..9f8724a 100644 --- a/lib/admin.ts +++ b/lib/admin.ts @@ -28,11 +28,8 @@ export type AdminUserDetail = { export type Page = { items: T[]; total: number; page: number; limit: number; pages: number }; async function request(path: string, options: RequestInit = {}): Promise { - const response = await fetch(`${apiBase}${path}`, { - credentials: "include", - headers: { "Content-Type": "application/json", ...(options.headers ?? {}) }, - ...options, - }); + const headers = { "Content-Type": "application/json", ...(options.headers as Record | undefined) }; + const response = await fetch(`${apiBase}${path}`, { credentials: "include", ...options, headers }); if (response.status === 401 || response.status === 403) { if (response.status === 403) throw new Error("无权执行此操作"); throw new Error("请登录"); @@ -43,6 +40,7 @@ async function request(path: string, options: RequestInit = {}): Promise { const msg = Array.isArray(message) ? (message as Array<{ msg?: string }>).map((m) => m.msg ?? "").join("; ") : (message as string); throw new Error(msg || "请求失败"); } + if (response.status === 204) return undefined as T; return response.json() as Promise; } @@ -52,6 +50,11 @@ export async function fetchCsrf(): Promise { return body.csrf_token ?? ""; } +export async function logout(): Promise { + const csrf = await fetchCsrf(); + await request("/auth/logout", { method: "POST", headers: { "X-CSRF-Token": csrf } }); +} + export async function getMe(): Promise<{ email: string; role: string; plan: string; status: string }> { return request("/auth/me"); }