为 new-api (LLM API 网关) 构建独立前端管理面板: - React 18 + TypeScript + Vite + Ant Design 5 前端 - Node.js + Express + better-sqlite3 BFF 后端 - 登录页: 站点选择器 + UserID + AccessToken 认证 - 仪表盘: 用户信息、额度环形图、7天趋势图、模型饼图、令牌概览、日志时间线 - 账单页: 筛选日志表格、模型消耗柱状图、充值记录、CSV/PDF 导出(HMAC签名) - 管理员站点管理: 站点 CRUD - API 代理: 多站点切换,会话管理 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
53 lines
1.3 KiB
TypeScript
53 lines
1.3 KiB
TypeScript
import { create } from 'zustand'
|
|
|
|
interface UserInfo {
|
|
id: number
|
|
username: string
|
|
display_name: string
|
|
role: number
|
|
status: number
|
|
email: string
|
|
group: string
|
|
quota: number
|
|
used_quota: number
|
|
request_count: number
|
|
aff_code: string
|
|
inviter_id: number
|
|
}
|
|
|
|
interface SiteInfo {
|
|
id: number
|
|
name: string
|
|
url: string
|
|
}
|
|
|
|
interface AuthState {
|
|
sessionToken: string | null
|
|
userInfo: UserInfo | null
|
|
site: SiteInfo | null
|
|
isLoggedIn: boolean
|
|
loading: boolean
|
|
login: (sessionToken: string, userInfo: UserInfo, site: SiteInfo) => void
|
|
logout: () => void
|
|
updateUserInfo: (userInfo: UserInfo) => void
|
|
setLoading: (loading: boolean) => void
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set) => ({
|
|
sessionToken: localStorage.getItem('sessionToken'),
|
|
userInfo: null,
|
|
site: null,
|
|
isLoggedIn: !!localStorage.getItem('sessionToken'),
|
|
loading: true,
|
|
login: (sessionToken, userInfo, site) => {
|
|
localStorage.setItem('sessionToken', sessionToken)
|
|
set({ sessionToken, userInfo, site, isLoggedIn: true, loading: false })
|
|
},
|
|
logout: () => {
|
|
localStorage.removeItem('sessionToken')
|
|
set({ sessionToken: null, userInfo: null, site: null, isLoggedIn: false, loading: false })
|
|
},
|
|
updateUserInfo: (userInfo) => set({ userInfo }),
|
|
setLoading: (loading) => set({ loading }),
|
|
}))
|