Files
one-api-workers/frontend/src/store/auth.ts
T
Jeff Gu bd76a89ba1 feat(auth): 新增系统设置功能
- 实现管理员登录和验证码验证功能
- 添加管理员登录流程,支持通过 Telegram 验证
- 实现登录挑战和会话管理,确保安全性
- 更新前端状态管理,处理登录和登出逻辑
- 增加系统配置管理,支持 Telegram 安全设置
- 新增数据库表以存储登录挑战和会话信息
2026-04-13 19:07:16 +08:00

120 lines
3.3 KiB
TypeScript

import { create } from 'zustand'
import { apiClient } from '@/api/client'
import { clearScopedCacheByPrefix } from '@/lib/local-cache'
import { type AdminLoginResponse } from '@/types'
import {
clearAdminCredentials,
getStoredAdminCredential,
storeAdminSessionToken,
} from '@/lib/admin-auth'
interface AuthState {
isAuthenticated: boolean
isLoading: boolean
error: string | null
showAuthModal: boolean
startLogin: (token: string) => Promise<AdminLoginResponse>
verifyLogin: (challengeId: string, code: string) => Promise<void>
logout: () => Promise<void>
checkAuth: () => Promise<void>
openAuthModal: () => void
closeAuthModal: () => void
}
export const useAuthStore = create<AuthState>((set) => ({
isAuthenticated: false,
isLoading: true,
error: null,
showAuthModal: false,
startLogin: async (token: string) => {
set({ isLoading: true, error: null })
try {
const response = await apiClient.startAdminLogin(token)
const loginResult = response.data as AdminLoginResponse
if (!loginResult) {
throw new Error('登录响应无效')
}
if (loginResult.sessionToken) {
storeAdminSessionToken(loginResult.sessionToken)
set({ isAuthenticated: true, isLoading: false, showAuthModal: false })
return loginResult
}
if (!loginResult.requiresVerification || !loginResult.challengeId) {
throw new Error('登录响应缺少验证码挑战信息')
}
set({ isAuthenticated: false, isLoading: false })
return loginResult
} catch (error) {
clearAdminCredentials()
set({
isAuthenticated: false,
isLoading: false,
error: error instanceof Error ? error.message : 'Authentication failed',
})
throw error
}
},
verifyLogin: async (challengeId: string, code: string) => {
set({ isLoading: true, error: null })
try {
const response = await apiClient.verifyAdminLogin(challengeId, code)
const loginResult = response.data as AdminLoginResponse
if (!loginResult.sessionToken) {
throw new Error('登录响应缺少会话令牌')
}
storeAdminSessionToken(loginResult.sessionToken)
set({ isAuthenticated: true, isLoading: false, showAuthModal: false })
} catch (error) {
clearAdminCredentials()
set({
isAuthenticated: false,
isLoading: false,
error: error instanceof Error ? error.message : 'Verification failed',
})
throw error
}
},
logout: async () => {
try {
await apiClient.logoutAdmin()
} catch {
// ignore logout errors and clear local state anyway
}
clearScopedCacheByPrefix('analytics:')
clearScopedCacheByPrefix('usage-logs:')
clearAdminCredentials()
set({ isAuthenticated: false, error: null })
},
checkAuth: async () => {
if (!getStoredAdminCredential()) {
set({ isAuthenticated: false, isLoading: false })
return
}
set({ isLoading: true })
try {
await apiClient.checkAuth()
set({ isAuthenticated: true, isLoading: false })
} catch (error) {
clearAdminCredentials()
set({ isAuthenticated: false, isLoading: false })
}
},
openAuthModal: () => set({ showAuthModal: true }),
closeAuthModal: () => set({ showAuthModal: false }),
}))