"use client"; import { useCallback, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import { toast } from "@heroui/react"; import { useSession, signOut } from "@/lib/auth-client"; export function SessionWatcher() { const router = useRouter(); const { data: session, isPending } = useSession(); const wasLoggedIn = useRef(false); const isHandling = useRef(false); const handleExpired = useCallback(async () => { if (isHandling.current) return; isHandling.current = true; try { await signOut({ fetchOptions: { credentials: "include" } }); } catch { // ignore sign-out errors } toast.warning("登录已过期,请重新登录"); router.push("/login"); }, [router]); // Detect session becoming null after being valid (e.g. server-side expiry) useEffect(() => { if (isPending) return; if (session) { wasLoggedIn.current = true; } else if (wasLoggedIn.current) { handleExpired(); } }, [session, isPending, handleExpired]); // Detect 401 responses from the API (dispatched by apiFetch) useEffect(() => { const handler = () => handleExpired(); window.addEventListener("session:expired", handler); return () => window.removeEventListener("session:expired", handler); }, [handleExpired]); return null; }