59 lines
1.7 KiB
TypeScript
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')
|
|
}
|