From f443e3e77b1430e5357fde442650b1dcdcf6d41e Mon Sep 17 00:00:00 2001 From: huangmin <2927933426@qq.com> Date: Thu, 4 Jun 2026 17:22:17 +0800 Subject: [PATCH] feat(auth): add backend logout API and integrate logout flow --- src/app/api/auth/logout/route.ts | 60 ++++++++++++++++++++++++++ src/components/auth/HeaderUserMenu.tsx | 27 +++++++++--- 2 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/app/api/auth/logout/route.ts diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts new file mode 100644 index 00000000..96b6be51 --- /dev/null +++ b/src/app/api/auth/logout/route.ts @@ -0,0 +1,60 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getRequestToken } from "@/app/api/_auth"; + +interface UpstreamAuthResponse { + data?: unknown; + message?: unknown; + status?: unknown; +} + +const UPSTREAM_LOGOUT_PATH = "/api_client/auth/logout"; + +function getUpstreamUrl(request: NextRequest): string { + const baseUrl = + process.env.POPIART_API_BASE_URL || + process.env.NEXT_PUBLIC_APP_URL || + request.nextUrl.origin; + return baseUrl.replace(/\/+$/, "") + UPSTREAM_LOGOUT_PATH; +} + +async function logout(request: NextRequest, method: "GET" | "POST") { + try { + const token = getRequestToken(request); + const headers: Record = { + Accept: "application/json", + }; + + if (token) { + headers.Authorization = `Bearer ${token}`; + headers.token = token; + } + + const response = await fetch(getUpstreamUrl(request), { + method, + headers, + cache: "no-store", + }); + + const responseBody = (await response.json()) as UpstreamAuthResponse; + return NextResponse.json(responseBody, { + status: response.status, + }); + } catch (error) { + console.error("Error logging out:", error); + return NextResponse.json( + { + status: "0500", + message: "Failed to logout", + }, + { status: 500 } + ); + } +} + +export async function POST(request: NextRequest) { + return logout(request, "POST"); +} + +export async function GET(request: NextRequest) { + return logout(request, "GET"); +} diff --git a/src/components/auth/HeaderUserMenu.tsx b/src/components/auth/HeaderUserMenu.tsx index 6072ee61..871ca131 100644 --- a/src/components/auth/HeaderUserMenu.tsx +++ b/src/components/auth/HeaderUserMenu.tsx @@ -10,11 +10,31 @@ import { useUserStore } from "@/store/userStore"; export function HeaderUserMenu() { const { t } = useI18n(); const [showUserMenu, setShowUserMenu] = useState(false); - const { userInfo, clearSession } = useUserStore(useShallow((state) => ({ + const { token, userInfo, clearSession } = useUserStore(useShallow((state) => ({ + token: state.token, userInfo: state.userInfo, clearSession: state.clearSession, }))); + const handleLogout = async () => { + try { + const headers = new Headers({ Accept: "application/json" }); + if (token?.trim()) { + headers.set("Authorization", `Bearer ${token.trim()}`); + headers.set("token", token.trim()); + } + + await fetch("/api/auth/logout", { + method: "POST", + headers, + cache: "no-store", + }); + } finally { + clearSession(); + setShowUserMenu(false); + } + }; + if (!userInfo) { return (