This commit is contained in:
rgrgogu
2026-05-23 14:03:48 +08:00
parent 768092901a
commit 56d984a26a
15 changed files with 1395 additions and 251 deletions
+49 -5
View File
@@ -7,7 +7,7 @@ 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 [accessToken, _setAccessToken] = useState(null)
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const [sessionRestored, setSessionRestored] = useState(false)
@@ -16,12 +16,12 @@ export function AuthProvider({ children }) {
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)
}, [])
// ── Login ──────────────────────────────────────────────────────────────────
const login = useCallback(async ({ email, password }) => {
setAuthError(null)
try {
@@ -36,6 +36,46 @@ export function AuthProvider({ children }) {
}
}, [])
// ── Register ───────────────────────────────────────────────────────────────
const register = useCallback(async (payload) => {
setAuthError(null)
try {
await api.post('/auth/register', payload)
return { success: true }
} catch (err) {
const message = err.response?.data?.message || 'Registration failed. Please try again.'
setAuthError(message)
return { success: false, message }
}
}, [])
// ── Verify OTP (auto-login) ────────────────────────────────────────────────
const verifyOTP = useCallback(async ({ email, otp }) => {
setAuthError(null)
try {
const { data } = await api.post('/auth/verify-otp', { email, otp })
setAccessToken(data.data.accessToken)
setUser(data.data.user)
return { success: true, user: data.data.user }
} catch (err) {
const message = err.response?.data?.message || 'OTP verification failed.'
setAuthError(message)
return { success: false, message }
}
}, [])
// ── Resend OTP ─────────────────────────────────────────────────────────────
const resendOTP = useCallback(async ({ email }) => {
try {
await api.post('/auth/resend-otp', { email })
return { success: true }
} catch (err) {
const message = err.response?.data?.message || 'Could not resend OTP.'
return { success: false, message }
}
}, [])
// ── Logout ─────────────────────────────────────────────────────────────────
const logout = useCallback(async () => {
try {
await api.post('/auth/logout')
@@ -47,12 +87,13 @@ export function AuthProvider({ children }) {
}
}, [])
// ── Restore session ────────────────────────────────────────────────────────
const restoreSession = useCallback(async () => {
if (isRestoring.current) return
isRestoring.current = true
try {
if (accessTokenRef.current) return // already have token, skip
if (accessTokenRef.current) return
const { data } = await api.post('/auth/refresh')
setAccessToken(data.data.accessToken)
setUser(data.data.user)
@@ -60,18 +101,21 @@ export function AuthProvider({ children }) {
setAccessToken(null)
setUser(null)
} finally {
setLoading(false) // ← set once, never back to true
setLoading(false)
}
}, [])
return (
<AuthContext.Provider value={{
accessToken,
accessTokenRef, // ← expose ref for interceptor
accessTokenRef,
setAccessToken,
user,
setUser,
login,
register,
verifyOTP,
resendOTP,
logout,
loading,
sessionRestored,