Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.env

# Logs
logs
*.log
Expand Down
2 changes: 1 addition & 1 deletion src/api/apiClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from 'axios';

const apiClient = axios.create({
baseURL: 'http://localhost:3000/api',
baseURL: import.meta.env.VITE_API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
Expand Down
16 changes: 9 additions & 7 deletions src/api/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import type { User } from '../../types/entities';
type LoginPayload = {mail: string; password_plaintext: string};
type RegisterPayload = Omit<User, 'password' | 'id' | 'role' | 'studentProfile' | 'professorProfile' | 'profile_picture'> & { password_plaintext: string };

interface AuthResponse {
token: string;
user: User;
}
const login = async (payload: LoginPayload): Promise<string> => {
const response = await apiClient.post<{ data: { token: string }}>('/auth/login', payload);

const login = async (payload: LoginPayload): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>('/auth/login', payload);
return response.data;
const token = response.data?.data?.token;

if (typeof token !== 'string' || token.length === 0) {
throw new Error('Token no fue recibido en la respuesta del servidor.');
}

return token;
};

const register = async (payload: RegisterPayload): Promise<User> => {
Expand Down
10 changes: 2 additions & 8 deletions src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ export interface AuthContextType {
isAuthenticated: boolean;
user: User | null;
isLoading: boolean;
login: (userData: User, token: string) => void;
login: (userData: User | null, token: string) => void;
logout: () => void;
}

export const AuthContext = createContext<AuthContextType>({
isAuthenticated: false,
user: null,
isLoading: true,
login: () => { throw new Error('Login function not implemented'); },
logout: () => { throw new Error('Logout function not implemented'); },
});
export const AuthContext = createContext<AuthContextType | null>(null);
36 changes: 8 additions & 28 deletions src/contexts/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,28 @@
import { useState, useEffect, useMemo } from 'react';
import { useState, useMemo, useCallback } from 'react';
import type { ReactNode } from 'react';
import type { User } from '../types/entities.ts';
import { authService } from '../api/services/auth.service.ts';
import { AuthContext } from './AuthContext';

export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const validateSession = async () => {
const token = localStorage.getItem('authToken');
if (token) {
try {
const userData = await authService.getProfile();
setUser(userData);
} catch (error) {
console.error("Session validation failed:", error);
localStorage.removeItem('authToken');
setUser(null);
}
}
setIsLoading(false);
};
validateSession();
}, []);

const login = (userData: User, token: string) => {
const login = useCallback((userData: User | null, token: string) => {
localStorage.setItem('authToken', token);
setUser(userData);
};
}, []);

const logout = () => {
const logout = useCallback(() => {
localStorage.removeItem('authToken');
setUser(null);
};
}, []);

const value = useMemo(() => ({
isAuthenticated: !!user,
isAuthenticated: !!user || !!localStorage.getItem('authToken'),
user,
isLoading,
isLoading: false,
login,
logout,
}), [user, isLoading]);
}), [user, login, logout]);

return (
<AuthContext.Provider value={value}>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { AuthContextType } from '../contexts/AuthContext';
export const useAuth = (): AuthContextType => {
const context = useContext(AuthContext);

if (context === undefined) {
if (context === undefined || context === null) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
Expand Down
17 changes: 13 additions & 4 deletions src/pages/Auth/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import type React from 'react';
import axios from 'axios';
import { Mail, Lock } from 'lucide-react';
import { useAuth } from '../../hooks/useAuth';
import { authService } from '../../api/services/auth.service';
Expand All @@ -26,12 +27,20 @@ const LoginPage = () => {
mail: credentials.mail,
password_plaintext: credentials.password,
};
const { user, token } = await authService.login(payload);
auth.login(user, token);
const token = await authService.login(payload);
if (!token || typeof token !== 'string') {
throw new Error("No se recibió un token válido del servidor.");
}
auth.login(null, token);
navigate('/');

} catch (err) {
setError('Credenciales incorrectas. Por favor, inténtalo de nuevo.');
console.error(err);
if (axios.isAxiosError(err)) {
setError(err.response?.data?.message || 'Credenciales incorrectas.');
} else {
setError('Ocurrió un error inesperado. Por favor, intenta de nuevo.');
}
console.error(err);
} finally {
setIsLoading(false);
}
Expand Down