Files
lingxi-web/src/app/store/auth.ts
T

59 lines
1.7 KiB
TypeScript

import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import type { AuthSession, Tenant, User } from '@/types'
import type { RoleKey } from '@/config/roles'
import { setTokenGetter, setUnauthorizedHandler } from '@/services/api/client'
interface AuthState {
token: string | null
user: User | null
tenant: Tenant | null
onboarded: boolean
setSession: (session: AuthSession) => void
logout: () => void
setOnboarded: (v: boolean) => void
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
token: null,
user: null,
tenant: null,
onboarded: true,
setSession: (session) =>
set({ token: session.token, user: session.user, tenant: session.tenant, onboarded: session.onboarded }),
logout: () => set({ token: null, user: null, tenant: null }),
setOnboarded: (v) => set({ onboarded: v }),
}),
{ name: 'lx-auth' },
),
)
// 让 API client 能读取 token(真实后端模式使用)
setTokenGetter(() => useAuthStore.getState().token)
setUnauthorizedHandler(() => {
useAuthStore.getState().logout()
if (!window.location.pathname.startsWith('/login')) {
window.location.replace('/login?reason=session-expired')
}
})
export function isSessionTokenExpired(token: string | null): boolean {
if (!token) return true
try {
const encoded = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')
const padded = encoded.padEnd(Math.ceil(encoded.length / 4) * 4, '=')
const payload = JSON.parse(window.atob(padded)) as {
exp?: number
}
return !payload.exp || payload.exp * 1_000 <= Date.now()
} catch {
return true
}
}
export function useCurrentRole(): RoleKey {
return useAuthStore((s) => s.user?.role ?? 'viewer')
}