Download web/src/lib/use-auth-guard.ts from zlcy/study: direct link, hf CLI and curl.
- Browser
- Download file 3.84 kB
-
https://huggingface.co/spaces/zlcy/study/resolve/main/web/src/lib/use-auth-guard.ts
- Command line
-
hf download hf://spaces/zlcy/study/web/src/lib/use-auth-guard.ts
-
curl -L -o use-auth-guard.ts https://huggingface.co/spaces/zlcy/study/resolve/main/web/src/lib/use-auth-guard.ts
3.84 kB
| "use client"; | |
| import { useEffect, useState } from "react"; | |
| import { useRouter } from "next/navigation"; | |
| import { getValidatedAuthSession } from "@/lib/auth-session"; | |
| import { | |
| getDefaultRouteForRole, | |
| type AuthRole, | |
| type StoredAuthSession, | |
| } from "@/store/auth"; | |
| type UseAuthGuardResult = { | |
| isCheckingAuth: boolean; | |
| session: StoredAuthSession | null; | |
| }; | |
| /** | |
| * 权限 / 登录态的兜底同步间隔。 | |
| * | |
| * ⚠️ 原来是 5 秒一次 **force** 校验(绕过 10 秒本地缓存,每次都实打服务端), | |
| * 这是全站最重的轮询接口 —— 线上实测 /auth/session 约 375 req/s,占全站请求 20%。 | |
| * 而权限和登录态本来就不会 5 秒一变,30 秒完全够用; | |
| * 另外下面还监听 focus / visibilitychange,用户切回标签页会**立刻**强制校验一次, | |
| * 所以真实感知比 30 秒更灵敏。 | |
| */ | |
| const AUTH_PERMISSION_SYNC_INTERVAL_MS = 30_000; | |
| function sameSession(left: StoredAuthSession | null, right: StoredAuthSession) { | |
| return Boolean( | |
| left | |
| && left.key === right.key | |
| && left.role === right.role | |
| && left.subjectId === right.subjectId | |
| && left.name === right.name | |
| && left.accountPoolEnabled === right.accountPoolEnabled | |
| && left.imageMemoryButtonEnabled === right.imageMemoryButtonEnabled | |
| && left.imageShareEvidenceEnabled === right.imageShareEvidenceEnabled | |
| && left.wechatOpenid === right.wechatOpenid | |
| && left.expiresAt === right.expiresAt, | |
| ); | |
| } | |
| export function useAuthGuard(allowedRoles?: AuthRole[]): UseAuthGuardResult { | |
| const router = useRouter(); | |
| const [session, setSession] = useState<StoredAuthSession | null>(null); | |
| const [isCheckingAuth, setIsCheckingAuth] = useState(true); | |
| const allowedRolesKey = (allowedRoles || []).join(","); | |
| useEffect(() => { | |
| let active = true; | |
| const load = async (force = false) => { | |
| const roleList = allowedRolesKey ? (allowedRolesKey.split(",") as AuthRole[]) : []; | |
| const storedSession = await getValidatedAuthSession({ force }); | |
| if (!active) { | |
| return; | |
| } | |
| if (!storedSession) { | |
| setSession(null); | |
| setIsCheckingAuth(false); | |
| router.replace("/login"); | |
| return; | |
| } | |
| if (roleList.length > 0 && !roleList.includes(storedSession.role)) { | |
| setSession(storedSession); | |
| setIsCheckingAuth(false); | |
| router.replace(getDefaultRouteForRole(storedSession.role)); | |
| return; | |
| } | |
| setSession((current) => sameSession(current, storedSession) ? current : storedSession); | |
| setIsCheckingAuth(false); | |
| }; | |
| void load(); | |
| const syncPermissions = () => { | |
| if (document.visibilityState === "visible") { | |
| void load(true); | |
| } | |
| }; | |
| const intervalId = window.setInterval(syncPermissions, AUTH_PERMISSION_SYNC_INTERVAL_MS); | |
| window.addEventListener("focus", syncPermissions); | |
| document.addEventListener("visibilitychange", syncPermissions); | |
| return () => { | |
| active = false; | |
| window.clearInterval(intervalId); | |
| window.removeEventListener("focus", syncPermissions); | |
| document.removeEventListener("visibilitychange", syncPermissions); | |
| }; | |
| }, [allowedRolesKey, router]); | |
| return { isCheckingAuth, session }; | |
| } | |
| export function useRedirectIfAuthenticated() { | |
| const router = useRouter(); | |
| const [isCheckingAuth, setIsCheckingAuth] = useState(true); | |
| useEffect(() => { | |
| let active = true; | |
| const load = async () => { | |
| const storedSession = await getValidatedAuthSession(); | |
| if (!active) { | |
| return; | |
| } | |
| if (storedSession) { | |
| router.replace(getDefaultRouteForRole(storedSession.role)); | |
| return; | |
| } | |
| setIsCheckingAuth(false); | |
| }; | |
| void load(); | |
| return () => { | |
| active = false; | |
| }; | |
| }, [router]); | |
| return { isCheckingAuth }; | |
| } | |