Files
nofx/web/src/contexts/AuthContext.tsx
SkywalkerJi b99a54ef41 fix: Fixed redundant key input fields and corrected formatting on the frontend. (#566)
* Eliminate redundant key input fields in the front-end.

* go / react Formatting.
2025-11-06 01:16:04 +08:00

328 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { createContext, useContext, useState, useEffect } from 'react'
import { getSystemConfig } from '../lib/config'
interface User {
id: string
email: string
}
interface AuthContextType {
user: User | null
token: string | null
login: (
email: string,
password: string
) => Promise<{
success: boolean
message?: string
userID?: string
requiresOTP?: boolean
}>
loginAdmin: (password: string) => Promise<{
success: boolean
message?: string
}>
register: (
email: string,
password: string,
betaCode?: string
) => Promise<{
success: boolean
message?: string
userID?: string
otpSecret?: string
qrCodeURL?: string
}>
verifyOTP: (
userID: string,
otpCode: string
) => Promise<{ success: boolean; message?: string }>
completeRegistration: (
userID: string,
otpCode: string
) => Promise<{ success: boolean; message?: string }>
resetPassword: (
email: string,
newPassword: string,
otpCode: string
) => Promise<{ success: boolean; message?: string }>
logout: () => void
isLoading: boolean
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [token, setToken] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// 先检查是否为管理员模式(使用带缓存的系统配置获取)
getSystemConfig()
.then(() => {
// 不再在管理员模式下模拟登录;统一检查本地存储
const savedToken = localStorage.getItem('auth_token')
const savedUser = localStorage.getItem('auth_user')
if (savedToken && savedUser) {
setToken(savedToken)
setUser(JSON.parse(savedUser))
}
setIsLoading(false)
})
.catch((err) => {
console.error('Failed to fetch system config:', err)
// 发生错误时,继续检查本地存储
const savedToken = localStorage.getItem('auth_token')
const savedUser = localStorage.getItem('auth_user')
if (savedToken && savedUser) {
setToken(savedToken)
setUser(JSON.parse(savedUser))
}
setIsLoading(false)
})
}, [])
const login = async (email: string, password: string) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
const data = await response.json()
if (response.ok) {
if (data.requires_otp) {
return {
success: true,
userID: data.user_id,
requiresOTP: true,
message: data.message,
}
}
} else {
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '登录失败,请重试' }
}
return { success: false, message: '未知错误' }
}
const loginAdmin = async (password: string) => {
try {
const response = await fetch('/api/admin-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password }),
})
const data = await response.json()
if (response.ok) {
const userInfo = {
id: data.user_id || 'admin',
email: data.email || 'admin@localhost',
}
setToken(data.token)
setUser(userInfo)
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(userInfo))
// 跳转到仪表盘
window.history.pushState({}, '', '/dashboard')
window.dispatchEvent(new PopStateEvent('popstate'))
return { success: true }
} else {
return { success: false, message: data.error || '登录失败' }
}
} catch (e) {
return { success: false, message: '登录失败,请重试' }
}
}
const register = async (
email: string,
password: string,
betaCode?: string
) => {
try {
const requestBody: {
email: string
password: string
beta_code?: string
} = { email, password }
if (betaCode) {
requestBody.beta_code = betaCode
}
const response = await fetch('/api/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
const data = await response.json()
if (response.ok) {
return {
success: true,
userID: data.user_id,
otpSecret: data.otp_secret,
qrCodeURL: data.qr_code_url,
message: data.message,
}
} else {
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '注册失败,请重试' }
}
}
const verifyOTP = async (userID: string, otpCode: string) => {
try {
const response = await fetch('/api/verify-otp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
})
const data = await response.json()
if (response.ok) {
// 登录成功保存token和用户信息
const userInfo = { id: data.user_id, email: data.email }
setToken(data.token)
setUser(userInfo)
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(userInfo))
// 跳转到配置页面
window.history.pushState({}, '', '/traders')
window.dispatchEvent(new PopStateEvent('popstate'))
return { success: true, message: data.message }
} else {
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: 'OTP验证失败请重试' }
}
}
const completeRegistration = async (userID: string, otpCode: string) => {
try {
const response = await fetch('/api/complete-registration', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user_id: userID, otp_code: otpCode }),
})
const data = await response.json()
if (response.ok) {
// 注册完成,自动登录
const userInfo = { id: data.user_id, email: data.email }
setToken(data.token)
setUser(userInfo)
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(userInfo))
// 跳转到配置页面
window.history.pushState({}, '', '/traders')
window.dispatchEvent(new PopStateEvent('popstate'))
return { success: true, message: data.message }
} else {
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '注册完成失败,请重试' }
}
}
const resetPassword = async (
email: string,
newPassword: string,
otpCode: string
) => {
try {
const response = await fetch('/api/reset-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
new_password: newPassword,
otp_code: otpCode,
}),
})
const data = await response.json()
if (response.ok) {
return { success: true, message: data.message }
} else {
return { success: false, message: data.error }
}
} catch (error) {
return { success: false, message: '密码重置失败,请重试' }
}
}
const logout = () => {
const savedToken = localStorage.getItem('auth_token')
if (savedToken) {
fetch('/api/logout', {
method: 'POST',
headers: { Authorization: `Bearer ${savedToken}` },
}).catch(() => {
/* ignore network errors on logout */
})
}
setUser(null)
setToken(null)
localStorage.removeItem('auth_token')
localStorage.removeItem('auth_user')
}
return (
<AuthContext.Provider
value={{
user,
token,
login,
loginAdmin,
register,
verifyOTP,
completeRegistration,
resetPassword,
logout,
isLoading,
}}
>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}