auth-context.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use client"
  2. import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'
  3. import { authApi } from './api'
  4. interface User {
  5. id: string
  6. username: string
  7. email?: string
  8. role: 'admin' | 'user'
  9. createdAt: string
  10. lastLogin?: string
  11. }
  12. interface AuthState {
  13. user: User | null
  14. token: string | null
  15. isAuthenticated: boolean
  16. isLoading: boolean
  17. error: string | null
  18. }
  19. interface AuthContextType extends AuthState {
  20. login: (username: string, password: string) => Promise<void>
  21. logout: () => void
  22. refreshToken: () => Promise<void>
  23. clearError: () => void
  24. }
  25. const AuthContext = createContext<AuthContextType | undefined>(undefined)
  26. export function AuthProvider({ children }: { children: ReactNode }) {
  27. const [state, setState] = useState<AuthState>({
  28. user: null,
  29. token: null,
  30. isAuthenticated: false,
  31. isLoading: true,
  32. error: null
  33. })
  34. useEffect(() => {
  35. // Check for existing token on mount
  36. const token = localStorage.getItem('auth_token')
  37. if (token) {
  38. validateToken(token)
  39. } else {
  40. setState(prev => ({ ...prev, isLoading: false }))
  41. }
  42. }, [])
  43. const validateToken = async (token: string) => {
  44. try {
  45. const data = await authApi.validateToken(token)
  46. setState({
  47. user: data.user,
  48. token,
  49. isAuthenticated: true,
  50. isLoading: false,
  51. error: null
  52. })
  53. localStorage.setItem('auth_token', token)
  54. } catch (error) {
  55. console.error('Token validation error:', error)
  56. localStorage.removeItem('auth_token')
  57. setState({
  58. user: null,
  59. token: null,
  60. isAuthenticated: false,
  61. isLoading: false,
  62. error: 'Session expired. Please log in again.'
  63. })
  64. }
  65. }
  66. const login = async (username: string, password: string) => {
  67. setState(prev => ({ ...prev, isLoading: true, error: null }))
  68. try {
  69. const data = await authApi.login(username, password)
  70. const { token, user } = data
  71. setState({
  72. user,
  73. token,
  74. isAuthenticated: true,
  75. isLoading: false,
  76. error: null
  77. })
  78. localStorage.setItem('auth_token', token)
  79. } catch (error) {
  80. setState(prev => ({
  81. ...prev,
  82. isLoading: false,
  83. error: error instanceof Error ? error.message : 'Login failed'
  84. }))
  85. }
  86. }
  87. const logout = () => {
  88. localStorage.removeItem('auth_token')
  89. setState({
  90. user: null,
  91. token: null,
  92. isAuthenticated: false,
  93. isLoading: false,
  94. error: null
  95. })
  96. }
  97. const refreshToken = async () => {
  98. const { token } = state
  99. if (!token) return
  100. try {
  101. const data = await authApi.refreshToken()
  102. const newToken = data.token
  103. setState(prev => ({ ...prev, token: newToken }))
  104. localStorage.setItem('auth_token', newToken)
  105. } catch (error) {
  106. console.error('Token refresh error:', error)
  107. logout()
  108. }
  109. }
  110. const clearError = () => {
  111. setState(prev => ({ ...prev, error: null }))
  112. }
  113. const value: AuthContextType = {
  114. ...state,
  115. login,
  116. logout,
  117. refreshToken,
  118. clearError
  119. }
  120. return (
  121. <AuthContext.Provider value={value}>
  122. {children}
  123. </AuthContext.Provider>
  124. )
  125. }
  126. export function useAuth(): AuthContextType {
  127. const context = useContext(AuthContext)
  128. if (context === undefined) {
  129. throw new Error('useAuth must be used within an AuthProvider')
  130. }
  131. return context
  132. }