diff --git a/src/hooks/useSyncMe.ts b/src/hooks/useSyncMe.ts index 5a15bbd..fa130fc 100644 --- a/src/hooks/useSyncMe.ts +++ b/src/hooks/useSyncMe.ts @@ -1,7 +1,7 @@ "use client"; import { useEffect, useRef } from "react"; -import { useSession } from "next-auth/react"; +import { useSession, signOut } from "next-auth/react"; import { useVoteStore } from "@/lib/store"; /** @@ -11,6 +11,10 @@ import { useVoteStore } from "@/lib/store"; * - status === "unauthenticated" → 清本地(避免上一个用户的票残留给下一个登录者) * - 切换用户(uid 变化) → 重新拉一次 * + * 僵尸 session 兜底:NextAuth 用 JWT 策略,cookie 不会因 DB user 被删而失效。 + * 当 /api/me 返回 401(签名失效) 或 NOT_FOUND(DB 里 user 已不存在) 时, + * 自动 signOut() 清 cookie —— 避免页面"假登录"假象(显示已登录但拉不到数据)。 + * * localStorage 仅作为本设备的缓存加速首屏渲染,服务端永远是唯一真相源。 */ export function useSyncMe() { @@ -45,12 +49,23 @@ export function useSyncMe() { credentials: "include", signal: ctrl.signal, }) - .then((r) => r.json()) - .then((res) => { + .then(async (r) => { if (cancelled) return; - if (res?.ok && Array.isArray(res.data?.votedArtists)) { + const res = await r.json().catch(() => null); + + if (r.ok && res?.ok && Array.isArray(res.data?.votedArtists)) { hydrateFromServer(res.data.votedArtists as string[]); lastSyncedUidRef.current = uid; + return; + } + + // 僵尸 session:JWT 还有效,但 DB 里 user 已不存在(或鉴权失效)。 + // 直接登出清 cookie,UI 状态切换为未登录,避免"显示已登录但拉不到数据"。 + const code: string | undefined = res?.error?.code; + if (r.status === 401 || code === "UNAUTHORIZED" || code === "NOT_FOUND") { + signOut({ redirect: false }); + reset(); + lastSyncedUidRef.current = null; } }) .catch(() => {