"use client"; import { useState, useEffect, useCallback, createContext, useContext } from "react"; import { api } from "@/lib/api"; import { hasNostrExtension, getPublicKey, signEvent, createAuthEvent, fetchNostrProfile, createBunkerSigner, type BunkerSignerInterface, type NostrProfile, } from "@/lib/nostr"; export interface User { pubkey: string; role: string; username?: string; name?: string; picture?: string; about?: string; nip05?: string; displayName?: string; } interface AuthContextType { user: User | null; loading: boolean; login: () => Promise; loginWithBunker: (input: string) => Promise; loginWithConnectedSigner: (signer: BunkerSignerInterface) => Promise; logout: () => void; isAdmin: boolean; isModerator: boolean; } export const AuthContext = createContext({ user: null, loading: true, login: async () => ({ pubkey: "", role: "USER" }), loginWithBunker: async () => ({ pubkey: "", role: "USER" }), loginWithConnectedSigner: async () => ({ pubkey: "", role: "USER" }), logout: () => {}, isAdmin: false, isModerator: false, }); export function useAuth() { return useContext(AuthContext); } export function useAuthProvider(): AuthContextType { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const stored = localStorage.getItem("bbe_user"); const token = localStorage.getItem("bbe_token"); if (stored && token) { try { setUser(JSON.parse(stored)); } catch { localStorage.removeItem("bbe_user"); localStorage.removeItem("bbe_token"); } } setLoading(false); }, []); const completeAuth = useCallback( async ( getPubKey: () => Promise, sign: (event: any) => Promise ): Promise => { const pubkey = await getPubKey(); const { challenge } = await api.getChallenge(pubkey); const event = createAuthEvent(pubkey, challenge); const signedEvent = await sign(event); const { token, user: userData } = await api.verify(pubkey, signedEvent); let profile: NostrProfile = {}; try { profile = await fetchNostrProfile(pubkey); } catch { // Profile fetch is best-effort } const fullUser: User = { ...userData, name: profile.name, displayName: profile.displayName, picture: profile.picture, about: profile.about, nip05: profile.nip05, username: userData.username, }; localStorage.setItem("bbe_token", token); localStorage.setItem("bbe_user", JSON.stringify(fullUser)); setUser(fullUser); return fullUser; }, [] ); const login = useCallback(async (): Promise => { if (!hasNostrExtension()) { throw new Error("Please install a Nostr extension (e.g., Alby, nos2x)"); } return completeAuth(getPublicKey, signEvent); }, [completeAuth]); const loginWithConnectedSigner = useCallback( async (signer: BunkerSignerInterface): Promise => { return completeAuth( () => signer.getPublicKey(), (event) => signer.signEvent(event) ); }, [completeAuth] ); const loginWithBunker = useCallback( async (input: string): Promise => { const { signer } = await createBunkerSigner(input); try { return await loginWithConnectedSigner(signer); } finally { await signer.close().catch(() => {}); } }, [loginWithConnectedSigner] ); const logout = useCallback(() => { localStorage.removeItem("bbe_token"); localStorage.removeItem("bbe_user"); setUser(null); }, []); return { user, loading, login, loginWithBunker, loginWithConnectedSigner, logout, isAdmin: user?.role === "ADMIN", isModerator: user?.role === "MODERATOR" || user?.role === "ADMIN", }; }