mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 07:54:22 +00:00
Migrate AuthContext
This commit is contained in:
@@ -1,25 +1,15 @@
|
||||
import {
|
||||
API_BASE_URL,
|
||||
User,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
isLoginResponse,
|
||||
isUser,
|
||||
} from '../types/authApi';
|
||||
import { API_BASE_URL, User, LoginRequest, isUser } from '../types/authApi';
|
||||
import { apiCall } from './api';
|
||||
|
||||
/**
|
||||
* Logs in a user with email and password
|
||||
* @param {string} email - The user's email
|
||||
* @param {string} password - The user's password
|
||||
* @returns {Promise<LoginResponse>} A promise that resolves to the login response
|
||||
* @returns {Promise<User>} A promise that resolves to the user
|
||||
* @throws {Error} If the API call fails or returns an invalid response
|
||||
* @throws {Error} If the login fails
|
||||
*/
|
||||
export const login = async (
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<LoginResponse> => {
|
||||
export const login = async (email: string, password: string): Promise<User> => {
|
||||
const loginData: LoginRequest = { email, password };
|
||||
const response = await apiCall(`${API_BASE_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
@@ -27,11 +17,11 @@ export const login = async (
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!isLoginResponse(data)) {
|
||||
throw new Error('Invalid login response received from API');
|
||||
if (!('user' in data) || !isUser(data.user)) {
|
||||
throw new Error('Invalid login response from API');
|
||||
}
|
||||
|
||||
return data;
|
||||
return data.user;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import * as authApi from '../api/auth';
|
||||
|
||||
const AuthContext = createContext(null);
|
||||
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
|
||||
// Load user data on mount
|
||||
useEffect(() => {
|
||||
const initializeAuth = async () => {
|
||||
try {
|
||||
const userData = await authApi.getCurrentUser();
|
||||
setUser(userData);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setInitialized(true);
|
||||
}
|
||||
};
|
||||
|
||||
initializeAuth();
|
||||
}, []);
|
||||
|
||||
const login = useCallback(async (email, password) => {
|
||||
try {
|
||||
const { user: userData } = await authApi.login(email, password);
|
||||
setUser(userData);
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: 'Logged in successfully',
|
||||
color: 'green',
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message || 'Login failed',
|
||||
color: 'red',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
try {
|
||||
await authApi.logout();
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
} finally {
|
||||
setUser(null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const refreshToken = useCallback(async () => {
|
||||
try {
|
||||
const success = await authApi.refreshToken();
|
||||
if (!success) {
|
||||
await logout();
|
||||
}
|
||||
return success;
|
||||
} catch (error) {
|
||||
console.error('Token refresh failed:', error);
|
||||
await logout();
|
||||
return false;
|
||||
}
|
||||
}, [logout]);
|
||||
|
||||
const refreshUser = useCallback(async () => {
|
||||
try {
|
||||
const userData = await authApi.getCurrentUser();
|
||||
setUser(userData);
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh user data:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const value = {
|
||||
user,
|
||||
loading,
|
||||
initialized,
|
||||
login,
|
||||
logout,
|
||||
refreshToken,
|
||||
refreshUser,
|
||||
};
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
131
app/src/contexts/AuthContext.tsx
Normal file
131
app/src/contexts/AuthContext.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import {
|
||||
login as apiLogin,
|
||||
logout as apiLogout,
|
||||
refreshToken as apiRefreshToken,
|
||||
getCurrentUser,
|
||||
} from '@/api/auth';
|
||||
import { User } from '@/types/authApi';
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
initialized: boolean;
|
||||
login: (email: string, password: string) => Promise<boolean>;
|
||||
logout: () => Promise<void>;
|
||||
refreshToken: () => Promise<boolean>;
|
||||
refreshUser: () => Promise<void>;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | null>(null);
|
||||
|
||||
interface AuthProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [initialized, setInitialized] = useState<boolean>(false);
|
||||
|
||||
// Load user data on mount
|
||||
useEffect(() => {
|
||||
const initializeAuth = async (): Promise<void> => {
|
||||
try {
|
||||
const userData = await getCurrentUser();
|
||||
setUser(userData);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setInitialized(true);
|
||||
}
|
||||
};
|
||||
|
||||
initializeAuth();
|
||||
}, []);
|
||||
|
||||
const login = useCallback(
|
||||
async (email: string, password: string): Promise<boolean> => {
|
||||
try {
|
||||
const userData = await apiLogin(email, password);
|
||||
setUser(userData);
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: 'Logged in successfully',
|
||||
color: 'green',
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error instanceof Error ? error.message : 'Login failed',
|
||||
color: 'red',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const logout = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
await apiLogout();
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
} finally {
|
||||
setUser(null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const refreshToken = useCallback(async (): Promise<boolean> => {
|
||||
try {
|
||||
const success = await apiRefreshToken();
|
||||
if (!success) {
|
||||
await logout();
|
||||
}
|
||||
return success;
|
||||
} catch (error) {
|
||||
console.error('Token refresh failed:', error);
|
||||
await logout();
|
||||
return false;
|
||||
}
|
||||
}, [logout]);
|
||||
|
||||
const refreshUser = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
const userData = await getCurrentUser();
|
||||
setUser(userData);
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh user data:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const value: AuthContextType = {
|
||||
user,
|
||||
loading,
|
||||
initialized,
|
||||
login,
|
||||
logout,
|
||||
refreshToken,
|
||||
refreshUser,
|
||||
};
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
};
|
||||
|
||||
export const useAuth = (): AuthContextType => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -72,31 +72,6 @@ export interface LoginRequest {
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login response from the API
|
||||
*/
|
||||
export interface LoginResponse {
|
||||
user: User;
|
||||
sessionId: string;
|
||||
expiresAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if a value is a valid LoginResponse
|
||||
*/
|
||||
export function isLoginResponse(value: unknown): value is LoginResponse {
|
||||
return (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
'user' in value &&
|
||||
isUser((value as LoginResponse).user) &&
|
||||
'sessionId' in value &&
|
||||
typeof (value as LoginResponse).sessionId === 'string' &&
|
||||
'expiresAt' in value &&
|
||||
typeof (value as LoginResponse).expiresAt === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* API call options extending the standard RequestInit
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user