Added admin page

This commit is contained in:
c-d-p
2025-04-22 00:10:57 +02:00
parent c0a58b45f4
commit bf147af3ef
11 changed files with 241 additions and 43 deletions

View File

@@ -5,9 +5,23 @@ import apiClient from '../api/client';
import { useTheme } from 'react-native-paper';
import { getAccessToken, getRefreshToken, storeTokens, clearTokens } from '../api/client';
// Define UserRole enum matching the backend
enum UserRole {
USER = 'user',
ADMIN = 'admin',
}
interface UserData {
username: string;
name: string;
role: UserRole;
// Add other user fields if needed
}
interface AuthContextData {
isAuthenticated: boolean;
isLoading: boolean;
user: UserData | null; // Add user data to context
login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}
@@ -15,6 +29,7 @@ interface AuthContextData {
const AuthContext = createContext<AuthContextData>({
isAuthenticated: false,
isLoading: true,
user: null, // Initialize user as null
login: async () => { throw new Error('AuthContext not initialized'); },
logout: async () => { throw new Error('AuthContext not initialized'); },
});
@@ -26,15 +41,45 @@ interface AuthProviderProps {
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isAuthenticatedState, setIsAuthenticatedState] = useState<boolean>(false);
const [userState, setUserState] = useState<UserData | null>(null); // State for user data
// Function to fetch user data
const fetchUserData = useCallback(async () => {
try {
console.log("[AuthContext] fetchUserData: Fetching /user/me");
const response = await apiClient.get<UserData>('/user/me');
console.log("[AuthContext] fetchUserData: User data received:", response.data);
setUserState(response.data);
return response.data;
} catch (error: any) {
console.error("[AuthContext] fetchUserData: Error fetching user data:", error.response?.data || error.message);
// If fetching user fails (e.g., token expired mid-session), log out
await clearTokens();
setIsAuthenticatedState(false);
setUserState(null);
return null;
}
}, []);
const checkAuthStatus = useCallback(async () => {
const token = await getAccessToken();
const hasToken = !!token;
if (hasToken !== isAuthenticatedState) {
setIsAuthenticatedState(hasToken);
if (hasToken) {
console.log("[AuthContext] checkAuthStatus: Token found, fetching user data.");
const userData = await fetchUserData();
if (userData) {
setIsAuthenticatedState(true);
} else {
// Fetch failed, already handled logout in fetchUserData
setIsAuthenticatedState(false);
}
} else {
console.log("[AuthContext] checkAuthStatus: No token found.");
setIsAuthenticatedState(false);
setUserState(null);
}
return hasToken;
}, [isAuthenticatedState]);
return isAuthenticatedState; // Return the updated state
}, [fetchUserData, isAuthenticatedState]); // Added isAuthenticatedState dependency
const loadInitialAuth = useCallback(async () => {
setIsLoading(true);
@@ -43,9 +88,10 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
await checkAuthStatus();
console.log("[AuthContext] loadInitialAuth: Initial check complete.");
} catch (error) {
console.error("[AuthContext] loadInitialAuth: Error loading initial token:", error);
console.error("[AuthContext] loadInitialAuth: Error during initial auth check:", error);
await clearTokens();
setIsAuthenticatedState(false);
setUserState(null);
} finally {
setIsLoading(false);
}
@@ -58,7 +104,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const login = useCallback(async (username: string, password: string) => {
console.log("[AuthContext] login: Function called with:", username);
try {
console.log("[AuthContext] login: Preparing to call apiClient.post for /auth/login");
// ... (existing login API call) ...
const response = await apiClient.post(
'/auth/login',
'grant_type=password&username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password) + '&scope=&client_id=&client_secret=',
@@ -70,10 +116,8 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
}
);
console.log("[AuthContext] login: apiClient.post successful, response status:", response?.status);
const { access_token, refresh_token } = response.data;
console.log("[AuthContext] login: Response data received.");
// ... (existing token validation) ...
if (!access_token || typeof access_token !== 'string' || !refresh_token) {
console.error("[AuthContext] login: Invalid token structure received:", response.data);
throw new Error('Invalid token received from server.');
@@ -81,29 +125,30 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
console.log('[AuthContext] login: Login successful, storing tokens.');
await storeTokens(access_token, refresh_token);
setIsAuthenticatedState(true);
// Fetch user data immediately after successful login
const userData = await fetchUserData();
if (userData) {
setIsAuthenticatedState(true);
} else {
// Should not happen if login succeeded, but handle defensively
throw new Error('Failed to fetch user data after login.');
}
} catch (error: any) {
// ... (existing error handling) ...
console.error("[AuthContext] login: Caught Error Object:", error);
if (error.isAxiosError) {
console.error("[AuthContext] login: Axios Error Details:");
console.error(" Request Config:", error.config);
console.error(" Response:", error.response?.status, error.response?.data);
console.error(" Message:", error.message);
}
await clearTokens();
setIsAuthenticatedState(false);
setUserState(null);
throw error;
}
}, []);
}, [fetchUserData]); // Added fetchUserData dependency
const logout = useCallback(async () => {
console.log('[AuthContext] logout: Logging out.');
const refreshToken = await getRefreshToken();
if (!refreshToken) {
console.warn('[AuthContext] logout: No refresh token found to send to backend.');
}
// ... (existing backend logout call) ...
try {
if (refreshToken) {
console.log('[AuthContext] logout: Calling backend /auth/logout');
@@ -115,6 +160,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
} finally {
await clearTokens();
setIsAuthenticatedState(false);
setUserState(null); // Clear user data on logout
console.log('[AuthContext] logout: Local tokens cleared and state updated.');
}
}, []);
@@ -122,10 +168,12 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const contextValue = useMemo(() => ({
isAuthenticated: isAuthenticatedState,
isLoading,
user: userState, // Provide user state
login,
logout,
}), [isAuthenticatedState, isLoading, login, logout]);
}), [isAuthenticatedState, isLoading, userState, login, logout]); // Added userState dependency
// ... (rest of the component: Provider, useAuth, AuthLoadingScreen) ...
return (
<AuthContext.Provider value={contextValue}>
{children}
@@ -147,4 +195,7 @@ export const AuthLoadingScreen: React.FC = () => {
container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: theme.colors.background }
});
return (<View style={styles.container}><ActivityIndicator size="large" color={theme.colors.primary} /></View>);
}
}
// Export UserRole if needed elsewhere
export { UserRole };