"use client"; import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { Ban, LogOut } from "lucide-react"; import { onForceLogout } from "@/lib/userEvents"; import { onDocumentVisible } from "@/lib/visibilityReconcile"; import { hasAuthCookieHint } from "@/lib/cookies"; import { apiMe } from "@/lib/api"; import { realtime, RT_SESSION_REPLACED } from "@/lib/realtime"; import Modal from "@/components/Modal"; /** * 账号安全守卫:监听全局强制下线事件,弹出不可误关的告知模态。 * * 两类告知: * - 账号被封禁(reason=banned):管理员封禁后由 HTTP 拦截链路派发; * - 登录已失效(reason=session_replaced):安全设置剔除本设备、 * 会话数超上限被淘汰等场景,旧浏览器当场弹窗提示,而非等硬刷新才发现。 * * 登录态被顶的感知入口(探测统一收敛在 lib/api 的 apiMe 判定): * 1) 后端 WS 即时事件 session:replaced(旧设备 WS 在线时秒级感知); * 2) WS 心跳复查失败 4401(realtime 内部转一次 /api/me 探测); * 3) 回前台对账:后台标签页错过 WS 事件,切回时经 /api/me 校正。 * * 登录态清理(cookie/Header 游客化/router.refresh)由 Header 复用统一 * 登出链路完成;本组件只负责"告知",故不处理任何凭据逻辑。 * 挂载于 root layout,覆盖任意页面(含后台)。 */ export default function AccountGuard() { const router = useRouter(); const [banned, setBanned] = useState(false); const [sessionEnded, setSessionEnded] = useState(false); useEffect( () => onForceLogout((reason) => { if (reason === "banned") setBanned(true); else if (reason === "session_replaced") setSessionEnded(true); }), [] ); // 登录态被顶:后端 WS 即时事件 → 探测确认 → 派发 session_replaced useEffect( () => realtime.on(RT_SESSION_REPLACED, () => void apiMe().catch(() => {})), [] ); // 回前台对账(10s 限频):覆盖后台标签页错过 WS 事件的场景。 // 仅当存在登录 cookie 痕迹时探测,游客不白发请求 useEffect( () => onDocumentVisible(() => { if (hasAuthCookieHint()) void apiMe().catch(() => {}); }), [] ); return ( <> {}} hideClose closeOnEscape={false} role="alertdialog" ariaLabel="账号已被封禁" maxWidthClass="max-w-[400px]" footer={ { setBanned(false); router.push("/"); }} className="btn btn-primary w-full" autoFocus > 我知道了 } header={ 账号已被封禁 } > 你的账号因违反社区规则已被管理员封禁,当前登录状态已失效,无法继续发布内容与互动。 如对处理结果有异议,请联系管理员核实。 {}} hideClose closeOnEscape={false} role="alertdialog" ariaLabel="登录已失效" maxWidthClass="max-w-[400px]" footer={ { setSessionEnded(false); router.push("/login"); }} className="btn btn-primary w-full" autoFocus > 重新登录 } header={ 登录已失效 } > 你的登录会话已失效(在安全设置中剔除、会话数超出上限被淘汰,或登录有效期已过)。 请重新登录后继续使用。 > ); }
你的账号因违反社区规则已被管理员封禁,当前登录状态已失效,无法继续发布内容与互动。 如对处理结果有异议,请联系管理员核实。
你的登录会话已失效(在安全设置中剔除、会话数超出上限被淘汰,或登录有效期已过)。 请重新登录后继续使用。