166 lines
4.2 KiB
TypeScript
166 lines
4.2 KiB
TypeScript
import { setApiAccessToken } from '@/lib/api-client';
|
|
import { createAuthClient } from 'better-auth/react';
|
|
import { emailOTPClient } from 'better-auth/client/plugins';
|
|
import type { User, Session } from 'better-auth/types';
|
|
import type { PropsWithChildren } from 'react';
|
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
|
|
const authClient = createAuthClient({
|
|
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000',
|
|
plugins: [emailOTPClient()],
|
|
});
|
|
|
|
type SignInParams = {
|
|
email: string;
|
|
password: string;
|
|
};
|
|
|
|
type SignUpParams = {
|
|
email: string;
|
|
password: string;
|
|
name: string;
|
|
};
|
|
|
|
type UpdateProfileParams = {
|
|
fullName: string;
|
|
};
|
|
|
|
type UpdatePasswordParams = {
|
|
password: string;
|
|
};
|
|
|
|
type AuthUser = User | null;
|
|
type AuthSession = Session | null;
|
|
|
|
export type AuthContextValue = {
|
|
user: AuthUser;
|
|
session: AuthSession;
|
|
loading: boolean;
|
|
isAuthenticated: boolean;
|
|
displayName: string;
|
|
initials: string;
|
|
avatarUrl: string | null;
|
|
signInWithPassword: (params: SignInParams) => Promise<void>;
|
|
signUp: (params: SignUpParams) => Promise<void>;
|
|
updateProfile: (params: UpdateProfileParams) => Promise<void>;
|
|
updatePassword: (params: UpdatePasswordParams) => Promise<void>;
|
|
signOut: () => Promise<void>;
|
|
};
|
|
|
|
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
|
|
function getDisplayName(user: AuthUser): string {
|
|
if (!user) return 'Usuario';
|
|
|
|
const name = user.name ?? user.email ?? 'Usuario';
|
|
return name;
|
|
}
|
|
|
|
function getInitials(name: string): string {
|
|
const words = name.trim().split(/\s+/).slice(0, 2);
|
|
const initials = words.map((word) => word[0]?.toUpperCase() ?? '').join('');
|
|
return initials || 'U';
|
|
}
|
|
|
|
function getAvatarUrl(user: AuthUser): string | null {
|
|
if (!user) return null;
|
|
return user.image ?? null;
|
|
}
|
|
|
|
export function AuthProvider({ children }: PropsWithChildren) {
|
|
const sessionStore = authClient.useSession();
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
setLoading(sessionStore.isPending);
|
|
}, [sessionStore.isPending]);
|
|
|
|
const user = sessionStore.data?.user ?? null;
|
|
const session = sessionStore.data?.session ?? null;
|
|
const isAuthenticated = !!session;
|
|
|
|
useEffect(() => {
|
|
if (sessionStore.data?.session?.token) {
|
|
setApiAccessToken(sessionStore.data.session.token);
|
|
} else {
|
|
setApiAccessToken(null);
|
|
}
|
|
}, [sessionStore.data?.session?.token]);
|
|
|
|
const value = useMemo<AuthContextValue>(() => {
|
|
const displayName = getDisplayName(user);
|
|
const initials = getInitials(displayName);
|
|
const avatarUrl = getAvatarUrl(user);
|
|
|
|
return {
|
|
user,
|
|
session,
|
|
loading,
|
|
isAuthenticated,
|
|
displayName,
|
|
initials,
|
|
avatarUrl,
|
|
signInWithPassword: async ({ email, password }) => {
|
|
const result = await authClient.signIn.email({
|
|
email,
|
|
password,
|
|
});
|
|
|
|
if (result.error) {
|
|
throw new Error(result.error.message);
|
|
}
|
|
},
|
|
signUp: async ({ email, password, name }) => {
|
|
const result = await authClient.signUp.email({
|
|
email,
|
|
password,
|
|
name,
|
|
});
|
|
|
|
if (result.error) {
|
|
throw new Error(result.error.message);
|
|
}
|
|
},
|
|
updateProfile: async ({ fullName }) => {
|
|
const result = await (authClient as any).user.update({
|
|
name: fullName,
|
|
});
|
|
|
|
if (result?.error) {
|
|
throw new Error(result.error.message);
|
|
}
|
|
},
|
|
updatePassword: async ({ password }) => {
|
|
const result = await (authClient as any).user.update({
|
|
password,
|
|
});
|
|
|
|
if (result?.error) {
|
|
throw new Error(result.error.message);
|
|
}
|
|
},
|
|
signOut: async () => {
|
|
const result = await authClient.signOut();
|
|
|
|
if (result.error) {
|
|
throw new Error(result.error.message);
|
|
}
|
|
},
|
|
};
|
|
}, [loading, session, user, isAuthenticated, sessionStore]);
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
}
|
|
|
|
export function useAuth() {
|
|
const context = useContext(AuthContext);
|
|
|
|
if (!context) {
|
|
throw new Error('useAuth must be used within AuthProvider');
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
export { authClient };
|