This commit is contained in:
rgrgogu
2026-05-05 23:16:22 +08:00
parent 683b25956a
commit 745c51682e
125 changed files with 18925 additions and 2 deletions
+87
View File
@@ -0,0 +1,87 @@
// contexts/AuthContext.jsx
import { createContext, useContext, useState, useCallback, useRef } from 'react'
import api from '../utils/api.util'
const AuthContext = createContext(null)
export const decodeToken = (token) => JSON.parse(atob(token.split('.')[1]))
export function AuthProvider({ children }) {
const [accessToken, _setAccessToken] = useState(null) // ← renamed to _setAccessToken
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const [sessionRestored, setSessionRestored] = useState(false)
const [authError, setAuthError] = useState(null)
const isRestoring = useRef(false)
const accessTokenRef = useRef(null)
// ← Single setter that updates both ref and state
const setAccessToken = useCallback((token) => {
accessTokenRef.current = token
_setAccessToken(token)
}, [])
const login = useCallback(async ({ email, password }) => {
setAuthError(null)
try {
const { data } = await api.post('/auth/login', { email, password })
setAccessToken(data.data.accessToken)
setUser(data.data.user)
return { success: true, user: data.data.user }
} catch (err) {
const message = err.response?.data?.message || 'Login failed. Please try again.'
setAuthError(message)
return { success: false, message }
}
}, [])
const logout = useCallback(async () => {
try {
await api.post('/auth/logout')
} catch (_) {
// ignore
} finally {
setAccessToken(null)
setUser(null)
}
}, [])
const restoreSession = useCallback(async () => {
if (isRestoring.current) return
isRestoring.current = true
try {
if (accessTokenRef.current) return // already have token, skip
const { data } = await api.post('/auth/refresh')
setAccessToken(data.data.accessToken)
setUser(data.data.user)
} catch (_) {
setAccessToken(null)
setUser(null)
} finally {
setLoading(false) // ← set once, never back to true
}
}, [])
return (
<AuthContext.Provider value={{
accessToken,
accessTokenRef, // ← expose ref for interceptor
setAccessToken,
user,
setUser,
login,
logout,
loading,
sessionRestored,
restoreSession,
authError,
setAuthError,
}}>
{children}
</AuthContext.Provider>
)
}
export const useAuth = () => useContext(AuthContext)