update error

This commit is contained in:
5t4l1n
2025-07-29 00:13:52 +05:30
parent 8816091e63
commit f72bcc69aa
7 changed files with 670 additions and 341 deletions
+118 -88
View File
@@ -5,7 +5,6 @@ import detectEthereumProvider from "@metamask/detect-provider"
import { ethers } from "ethers"
import { toast } from "react-hot-toast"
import api from "@/lib/api"
import type { AuthNonceRequest, AuthNonceResponse, AuthVerifyRequest, AuthVerifyResponse, User } from "@/lib/types"
import { auth } from "@/lib/firebase"
import {
signInWithEmailAndPassword,
@@ -15,12 +14,24 @@ import {
type User as FirebaseUser,
} from "firebase/auth"
interface User {
id: string
wallet_address: string
name?: string
bio?: string
avatar?: string
created_at: string
last_login: string
}
interface AuthContextType {
user: User | null // MetaMask user
firebaseUser: FirebaseUser | null // Firebase user
token: string | null // JWT token from backend (only for MetaMask users)
user: User | null
firebaseUser: FirebaseUser | null
token: string | null
isLoadingAuth: boolean
authMethod: "metamask" | "firebase" | null
walletAddress: string | null
walletConnected: boolean
connectWallet: () => Promise<void>
loginWithEmail: (email: string, password: string) => Promise<void>
signupWithEmail: (email: string, password: string) => Promise<void>
@@ -30,39 +41,39 @@ interface AuthContextType {
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null) // For MetaMask user
const [firebaseUser, setFirebaseUser] = useState<FirebaseUser | null>(null) // For Firebase user
const [token, setToken] = useState<string | null>(null) // JWT token
const [user, setUser] = useState<User | null>(null)
const [firebaseUser, setFirebaseUser] = useState<FirebaseUser | null>(null)
const [token, setToken] = useState<string | null>(null)
const [isLoadingAuth, setIsLoadingAuth] = useState(true)
const [authMethod, setAuthMethod] = useState<"metamask" | "firebase" | null>(null)
const [walletAddress, setWalletAddress] = useState<string | null>(null)
const [walletConnected, setWalletConnected] = useState(false)
// Initialize auth state
useEffect(() => {
// Check for MetaMask token
const storedToken = localStorage.getItem("openlearnx_jwt_token")
const storedUser = localStorage.getItem("openlearnx_user")
if (storedToken && storedUser) {
const storedWallet = localStorage.getItem("openlearnx_wallet")
if (storedToken && storedUser && storedWallet) {
try {
setUser(JSON.parse(storedUser))
setToken(storedToken)
setWalletAddress(storedWallet)
setWalletConnected(true)
setAuthMethod("metamask")
} catch (error) {
console.error("Failed to parse stored MetaMask user or token:", error)
localStorage.removeItem("openlearnx_jwt_token")
localStorage.removeItem("openlearnx_user")
localStorage.clear()
}
}
// Listen for Firebase auth state changes
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
if (currentUser) {
if (currentUser && authMethod !== "metamask") {
setFirebaseUser(currentUser)
setAuthMethod("firebase")
} else {
} else if (!currentUser && authMethod === "firebase") {
setFirebaseUser(null)
if (authMethod !== "metamask") {
// Only clear if not already MetaMask authenticated
setAuthMethod(null)
}
setAuthMethod(null)
}
setIsLoadingAuth(false)
})
@@ -70,26 +81,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
return () => unsubscribe()
}, [authMethod])
const logout = useCallback(async () => {
setUser(null)
setFirebaseUser(null)
setToken(null)
setAuthMethod(null)
localStorage.removeItem("openlearnx_jwt_token")
localStorage.removeItem("openlearnx_user")
try {
await signOut(auth) // Sign out from Firebase
} catch (error) {
console.error("Error signing out from Firebase:", error)
}
toast.success("Logged out successfully!")
}, [])
const connectWallet = useCallback(async () => {
setIsLoadingAuth(true)
try {
const provider = await detectEthereumProvider()
if (!provider) {
toast.error("MetaMask not detected. Please install it.")
return
@@ -97,64 +93,81 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const ethProvider = new ethers.BrowserProvider(provider as any)
const accounts = await ethProvider.send("eth_requestAccounts", [])
if (accounts.length === 0) {
toast.error("No accounts connected.")
return
}
const walletAddress = accounts[0]
const walletAddr = accounts[0]
// 1. Request Nonce
const nonceResponse = await api.post<AuthNonceResponse>("/api/auth/nonce", {
wallet_address: walletAddress,
} as AuthNonceRequest)
const { nonce, message } = nonceResponse.data
// Get nonce from backend
const nonceResponse = await api.post("/api/auth/nonce", {
wallet_address: walletAddr,
})
if (!nonceResponse.data.success) {
throw new Error(nonceResponse.data.error || "Failed to get nonce")
}
// 2. Sign Message
const { message } = nonceResponse.data
// Sign message
const signer = await ethProvider.getSigner()
const signature = await signer.signMessage(message)
// 3. Verify Signature
const verifyResponse = await api.post<AuthVerifyResponse>("/api/auth/verify", {
wallet_address: walletAddress,
// Verify signature
const verifyResponse = await api.post("/api/auth/verify", {
wallet_address: walletAddr,
signature,
message,
} as AuthVerifyRequest)
})
if (verifyResponse.data.success) {
const { token: newToken, user: newUser } = verifyResponse.data
setToken(newToken)
setUser(newUser)
setFirebaseUser(null) // Clear Firebase user if MetaMask logs in
const { token, user } = verifyResponse.data
// Update states
setToken(token)
setUser(user)
setWalletAddress(walletAddr)
setWalletConnected(true)
setFirebaseUser(null)
setAuthMethod("metamask")
localStorage.setItem("openlearnx_jwt_token", newToken)
localStorage.setItem("openlearnx_user", JSON.stringify(newUser))
toast.success(`Welcome, ${newUser.wallet_address.slice(0, 6)}...${newUser.wallet_address.slice(-4)}!`)
// Store in localStorage
localStorage.setItem("openlearnx_jwt_token", token)
localStorage.setItem("openlearnx_user", JSON.stringify(user))
localStorage.setItem("openlearnx_wallet", walletAddr)
toast.success(`Welcome! 🦊`)
// ✅ CRITICAL: Redirect to dashboard after successful login
setTimeout(() => {
window.location.href = "/dashboard"
}, 1000)
} else {
toast.error("MetaMask authentication failed. Please try again.")
logout()
throw new Error("Authentication failed")
}
} catch (error: any) {
console.error("MetaMask authentication error:", error)
toast.error(error.message || "Failed to connect wallet or authenticate.")
logout()
console.error("MetaMask error:", error)
toast.error(error.message || "Failed to connect MetaMask")
} finally {
setIsLoadingAuth(false)
}
}, [logout])
}, [])
const loginWithEmail = useCallback(async (email: string, password: string) => {
setIsLoadingAuth(true)
try {
await signInWithEmailAndPassword(auth, email, password)
// Firebase user is set by onAuthStateChanged listener
setUser(null) // Clear MetaMask user if Firebase logs in
setToken(null) // Clear JWT token
setUser(null)
setToken(null)
setWalletAddress(null)
setWalletConnected(false)
toast.success("Logged in with email!")
} catch (error: any) {
console.error("Firebase login error:", error)
toast.error(error.message || "Failed to login with email.")
toast.error(error.message || "Email login failed")
throw error
} finally {
setIsLoadingAuth(false)
}
@@ -164,40 +177,57 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setIsLoadingAuth(true)
try {
await createUserWithEmailAndPassword(auth, email, password)
// Firebase user is set by onAuthStateChanged listener
setUser(null) // Clear MetaMask user if Firebase logs in
setToken(null) // Clear JWT token
toast.success("Signed up and logged in with email!")
toast.success("Account created!")
} catch (error: any) {
console.error("Firebase signup error:", error)
toast.error(error.message || "Failed to sign up with email.")
toast.error(error.message || "Signup failed")
throw error
} finally {
setIsLoadingAuth(false)
}
}, [])
const contextValue = React.useMemo(
() => ({
user,
firebaseUser,
token,
isLoadingAuth,
authMethod,
connectWallet,
loginWithEmail,
signupWithEmail,
logout,
}),
[user, firebaseUser, token, isLoadingAuth, authMethod, connectWallet, loginWithEmail, signupWithEmail, logout],
)
const logout = useCallback(async () => {
setUser(null)
setFirebaseUser(null)
setToken(null)
setWalletAddress(null)
setWalletConnected(false)
setAuthMethod(null)
localStorage.clear()
try {
await signOut(auth)
} catch (error) {
console.error("Logout error:", error)
}
toast.success("Logged out!")
}, [])
return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
const value = {
user,
firebaseUser,
token,
isLoadingAuth,
authMethod,
walletAddress,
walletConnected,
connectWallet,
loginWithEmail,
signupWithEmail,
logout,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
if (!context) {
throw new Error("useAuth must be used within an AuthProvider")
}
return context
}
}
// ✅ CRITICAL: Default export to fix the "invalid element type" error
export default AuthProvider