Browse Source

feat(auth): add backend logout API and integrate logout flow

feature/group
huangmin 1 month ago
parent
commit
f443e3e77b
  1. 60
      src/app/api/auth/logout/route.ts
  2. 27
      src/components/auth/HeaderUserMenu.tsx

60
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<string, string> = {
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<UpstreamAuthResponse>(responseBody, {
status: response.status,
});
} catch (error) {
console.error("Error logging out:", error);
return NextResponse.json<UpstreamAuthResponse>(
{
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");
}

27
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 (
<button
@ -57,10 +77,7 @@ export function HeaderUserMenu() {
<button
type="button"
className="mt-3 w-full rounded-lg border border-neutral-800 px-3 py-2 text-left text-sm text-neutral-300 transition-colors hover:border-neutral-700 hover:bg-neutral-900 hover:text-neutral-100"
onClick={() => {
clearSession();
setShowUserMenu(false);
}}
onClick={() => void handleLogout()}
>
{t("auth.logout")}
</button>

Loading…
Cancel
Save