Added better auth. Still fixing issues

This commit is contained in:
Jose Selesan
2026-04-14 14:14:07 -03:00
parent e2299c6d46
commit 9133950d6a
42 changed files with 2453 additions and 1358 deletions

View File

@@ -1,14 +1,14 @@
import { setApiAccessToken } from '@/lib/api-client';
import { supabase } from '@/lib/supabase';
import type { Session, User } from '@supabase/supabase-js';
import {
type PropsWithChildren,
createContext,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
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;
@@ -18,7 +18,7 @@ type SignInParams = {
type SignUpParams = {
email: string;
password: string;
fullName: string;
name: string;
};
type UpdateProfileParams = {
@@ -29,16 +29,19 @@ type UpdatePasswordParams = {
password: string;
};
type AuthUser = User | null;
type AuthSession = Session | null;
export type AuthContextValue = {
user: User | null;
session: Session | null;
user: AuthUser;
session: AuthSession;
loading: boolean;
isAuthenticated: boolean;
displayName: string;
initials: string;
avatarUrl: string | null;
signInWithPassword: (params: SignInParams) => Promise<void>;
signUp: (params: SignUpParams) => Promise<{ requiresEmailConfirmation: boolean }>;
signUp: (params: SignUpParams) => Promise<void>;
updateProfile: (params: UpdateProfileParams) => Promise<void>;
updatePassword: (params: UpdatePasswordParams) => Promise<void>;
signOut: () => Promise<void>;
@@ -46,17 +49,11 @@ export type AuthContextValue = {
const AuthContext = createContext<AuthContextValue | null>(null);
function getDisplayName(user: User | null): string {
function getDisplayName(user: AuthUser): string {
if (!user) return 'Usuario';
const fromMetadata =
user.user_metadata?.name ?? user.user_metadata?.full_name ?? user.user_metadata?.user_name;
if (typeof fromMetadata === 'string' && fromMetadata.trim().length > 0) {
return fromMetadata.trim();
}
return user.email ?? 'Usuario';
const name = user.name ?? user.email ?? 'Usuario';
return name;
}
function getInitials(name: string): string {
@@ -65,48 +62,30 @@ function getInitials(name: string): string {
return initials || 'U';
}
function getAvatarUrl(user: User | null): string | null {
function getAvatarUrl(user: AuthUser): string | null {
if (!user) return null;
const value = user.user_metadata?.avatar_url ?? user.user_metadata?.picture;
return typeof value === 'string' && value.length > 0 ? value : null;
return user.image ?? null;
}
export function AuthProvider({ children }: PropsWithChildren) {
const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<User | null>(null);
const sessionStore = authClient.useSession();
const [loading, setLoading] = useState(true);
useEffect(() => {
let mounted = true;
setLoading(sessionStore.isPending);
}, [sessionStore.isPending]);
const init = async () => {
const { data } = await supabase.auth.getSession();
const user = sessionStore.data?.user ?? null;
const session = sessionStore.data?.session ?? null;
const isAuthenticated = !!session;
if (!mounted) return;
setSession(data.session);
setUser(data.session?.user ?? null);
setApiAccessToken(data.session?.access_token ?? null);
setLoading(false);
};
void init();
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, nextSession) => {
setSession(nextSession);
setUser(nextSession?.user ?? null);
setApiAccessToken(nextSession?.access_token ?? null);
setLoading(false);
});
return () => {
mounted = false;
subscription.unsubscribe();
};
}, []);
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);
@@ -117,69 +96,58 @@ export function AuthProvider({ children }: PropsWithChildren) {
user,
session,
loading,
isAuthenticated: Boolean(user),
isAuthenticated,
displayName,
initials,
avatarUrl,
signInWithPassword: async ({ email, password }) => {
const { error } = await supabase.auth.signInWithPassword({
const result = await authClient.signIn.email({
email,
password,
});
if (error) {
throw error;
if (result.error) {
throw new Error(result.error.message);
}
},
signUp: async ({ email, password, fullName }) => {
const { data, error } = await supabase.auth.signUp({
signUp: async ({ email, password, name }) => {
const result = await authClient.signUp.email({
email,
password,
options: {
data: {
full_name: fullName,
name: fullName,
},
},
name,
});
if (error) {
throw error;
if (result.error) {
throw new Error(result.error.message);
}
return {
requiresEmailConfirmation: !data.session,
};
},
updateProfile: async ({ fullName }) => {
const { error } = await supabase.auth.updateUser({
data: {
full_name: fullName,
name: fullName,
},
const result = await (authClient as any).user.update({
name: fullName,
});
if (error) {
throw error;
if (result?.error) {
throw new Error(result.error.message);
}
},
updatePassword: async ({ password }) => {
const { error } = await supabase.auth.updateUser({
const result = await (authClient as any).user.update({
password,
});
if (error) {
throw error;
if (result?.error) {
throw new Error(result.error.message);
}
},
signOut: async () => {
const { error } = await supabase.auth.signOut();
if (error) {
throw error;
const result = await authClient.signOut();
if (result.error) {
throw new Error(result.error.message);
}
},
};
}, [loading, session, user]);
}, [loading, session, user, isAuthenticated, sessionStore]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
@@ -193,3 +161,5 @@ export function useAuth() {
return context;
}
export { authClient };