refactor(logging): replace console usage in auth flows

This commit is contained in:
William Valentin
2025-09-23 10:15:57 -07:00
parent 6b6a44acef
commit c1c8e28f01
4 changed files with 104 additions and 32 deletions

39
App.tsx
View File

@@ -32,6 +32,7 @@ import {
AuthPage, AuthPage,
AvatarDropdown, AvatarDropdown,
ChangePasswordModal, ChangePasswordModal,
ResetPasswordPage,
} from './components/auth'; } from './components/auth';
import { AdminInterface } from './components/admin'; import { AdminInterface } from './components/admin';
import { import {
@@ -62,6 +63,8 @@ import {
import { useUser } from './contexts/UserContext'; import { useUser } from './contexts/UserContext';
import { databaseService } from './services/database'; import { databaseService } from './services/database';
import { databaseSeeder } from './services/database.seeder'; import { databaseSeeder } from './services/database.seeder';
import { logger } from './services/logging';
import { normalizeError } from './utils/error';
const Header: React.FC<{ const Header: React.FC<{
onAdd: () => void; onAdd: () => void;
@@ -230,7 +233,9 @@ const MedicationScheduleApp: React.FC<{ user: User }> = ({ user }) => {
useEffect(() => { useEffect(() => {
// Don't try to fetch data if user._id is not available // Don't try to fetch data if user._id is not available
if (!user._id) { if (!user._id) {
console.warn('Skipping data fetch: user._id is not available'); logger.ui.action('Skipping data fetch because user id is missing', {
userId: user._id,
});
return; return;
} }
@@ -239,7 +244,9 @@ const MedicationScheduleApp: React.FC<{ user: User }> = ({ user }) => {
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
console.warn('Fetching data for user:', user._id); logger.db.query('Fetching medication data for user', {
userId: user._id,
});
const [medsData, remindersData, takenDosesData, settingsData] = const [medsData, remindersData, takenDosesData, settingsData] =
await Promise.all([ await Promise.all([
@@ -249,9 +256,10 @@ const MedicationScheduleApp: React.FC<{ user: User }> = ({ user }) => {
databaseService.getUserSettings(user._id), databaseService.getUserSettings(user._id),
]); ]);
console.warn('Data fetched successfully:', { logger.db.query('Fetched user data successfully', {
medications: medsData.length, userId: user._id,
reminders: remindersData.length, medicationCount: medsData.length,
reminderCount: remindersData.length,
hasTakenDoses: !!takenDosesData, hasTakenDoses: !!takenDosesData,
hasSettings: !!settingsData, hasSettings: !!settingsData,
}); });
@@ -266,8 +274,9 @@ const MedicationScheduleApp: React.FC<{ user: User }> = ({ user }) => {
} }
} catch (e) { } catch (e) {
setError('Failed to load your data. Please try again.'); setError('Failed to load your data. Please try again.');
console.error('Error loading user data:', e); logger.db.error('Error loading user data', normalizeError(e), {
console.error('User object:', user); userId: user._id,
});
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@@ -688,7 +697,11 @@ const MedicationScheduleApp: React.FC<{ user: User }> = ({ user }) => {
setSettings(updatedSettings); setSettings(updatedSettings);
setOnboardingOpen(false); setOnboardingOpen(false);
} catch (error) { } catch (error) {
console.error('Failed to update onboarding status', error); logger.ui.error(
'Failed to update onboarding status',
normalizeError(error),
{ userId: user._id }
);
setOnboardingOpen(false); setOnboardingOpen(false);
} }
} }
@@ -910,10 +923,10 @@ const App: React.FC = () => {
useEffect(() => { useEffect(() => {
const runSeeding = async () => { const runSeeding = async () => {
try { try {
console.warn('🌱 Initializing database seeding...'); logger.db.query('Initializing database seeding');
await databaseSeeder.seedDatabase(); await databaseSeeder.seedDatabase();
} catch (error) { } catch (error) {
console.error(' Database seeding failed:', error); logger.db.error('Database seeding failed', normalizeError(error));
} }
}; };
@@ -929,6 +942,12 @@ const App: React.FC = () => {
} }
if (!user) { if (!user) {
if (
typeof window !== 'undefined' &&
window.location.pathname === '/reset-password'
) {
return <ResetPasswordPage />;
}
return <AuthPage />; return <AuthPage />;
} }

View File

@@ -9,8 +9,11 @@ import { User } from '../types';
import { databaseService } from '../services/database'; import { databaseService } from '../services/database';
import { authService } from '../services/auth/auth.service'; import { authService } from '../services/auth/auth.service';
import { tokenStorage } from '../utils/token'; import { tokenStorage } from '../utils/token';
import { logger } from '../services/logging';
import { normalizeError } from '../utils/error';
const SESSION_KEY = 'medication_app_session'; const SESSION_KEY = 'medication_app_session';
const AUTH_CONTEXT = 'USER_CONTEXT';
interface UserContextType { interface UserContextType {
user: User | null; user: User | null;
@@ -69,27 +72,23 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({
// Use auth service for password-based login // Use auth service for password-based login
const result = await authService.login({ email, password }); const result = await authService.login({ email, password });
console.warn('Login result received:', result);
console.warn('User from login:', result.user);
console.warn('User _id:', result.user._id);
// Update last login time // Update last login time
const updatedUser = { ...result.user, lastLoginAt: new Date() }; const updatedUser = { ...result.user, lastLoginAt: new Date() };
await databaseService.updateUser(updatedUser); await databaseService.updateUser(updatedUser);
console.warn('Updated user with last login:', updatedUser);
tokenStorage.save({ tokenStorage.save({
accessToken: result.accessToken, accessToken: result.accessToken,
refreshToken: result.refreshToken, refreshToken: result.refreshToken,
}); });
// Set the user from the login result // Set the user from the login result
setUser(updatedUser); setUser(updatedUser);
logger.auth.login('User authenticated with email/password', {
console.warn('User set in context'); userId: updatedUser._id,
email: updatedUser.email,
});
return true; return true;
} catch (error) { } catch (error) {
console.error('Login error:', error); logger.auth.error('Login error', normalizeError(error), { email });
return false; return false;
} }
}; };
@@ -104,7 +103,10 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({
// Don't auto-login after registration, require email verification // Don't auto-login after registration, require email verification
return true; return true;
} catch (error) { } catch (error) {
console.error('Registration error:', error); logger.auth.error('Registration error', normalizeError(error), {
email,
username,
});
return false; return false;
} }
}; };
@@ -116,26 +118,26 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({
try { try {
const result = await authService.loginWithOAuth(provider, userData); const result = await authService.loginWithOAuth(provider, userData);
console.warn('OAuth login result received:', result);
console.warn('OAuth user:', result.user);
console.warn('OAuth user _id:', result.user._id);
// Update last login time // Update last login time
const updatedUser = { ...result.user, lastLoginAt: new Date() }; const updatedUser = { ...result.user, lastLoginAt: new Date() };
await databaseService.updateUser(updatedUser); await databaseService.updateUser(updatedUser);
console.warn('Updated OAuth user with last login:', updatedUser);
tokenStorage.save({ tokenStorage.save({
accessToken: result.accessToken, accessToken: result.accessToken,
refreshToken: result.refreshToken, refreshToken: result.refreshToken,
}); });
setUser(updatedUser); setUser(updatedUser);
logger.auth.login('User authenticated via OAuth', {
console.warn('OAuth user set in context'); userId: updatedUser._id,
provider,
email: updatedUser.email,
});
return true; return true;
} catch (error) { } catch (error) {
console.error('OAuth login error:', error); logger.auth.error('OAuth login error', normalizeError(error), {
provider,
email: userData.email,
});
return false; return false;
} }
}; };
@@ -150,16 +152,21 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({
} }
await authService.changePassword(user._id, currentPassword, newPassword); await authService.changePassword(user._id, currentPassword, newPassword);
logger.auth.login('User changed password', { userId: user._id });
return true; return true;
} catch (error) { } catch (error) {
console.error('Password change error:', error); logger.auth.error('Password change error', normalizeError(error), {
userId: user?._id,
});
return false; return false;
} }
}; };
const logout = () => { const logout = () => {
const currentUserId = user?._id;
tokenStorage.clear(); tokenStorage.clear();
setUser(null); setUser(null);
logger.auth.logout('User logged out', { userId: currentUserId });
}; };
const updateUser = async (updatedUser: User) => { const updateUser = async (updatedUser: User) => {
@@ -167,8 +174,12 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({
const savedUser = await databaseService.updateUser(updatedUser); const savedUser = await databaseService.updateUser(updatedUser);
setUser(savedUser); setUser(savedUser);
} catch (error) { } catch (error) {
console.error('Failed to update user', error); logger.error(
// Optionally revert state or show error 'Failed to update user profile',
AUTH_CONTEXT,
{ userId: updatedUser._id },
normalizeError(error)
);
} }
}; };

View File

@@ -0,0 +1,27 @@
import { normalizeError } from '../error';
describe('normalizeError', () => {
it('returns the same error instance when provided', () => {
const error = new Error('test');
expect(normalizeError(error)).toBe(error);
});
it('converts strings into Error instances', () => {
const result = normalizeError('failure');
expect(result).toBeInstanceOf(Error);
expect(result.message).toBe('failure');
});
it('stringifies objects when creating the error message', () => {
const result = normalizeError({ reason: 'timeout', code: 504 });
expect(result.message).toBe('{"reason":"timeout","code":504}');
});
it('falls back to a generic error for unserializable input', () => {
const circular: Record<string, unknown> = {};
circular.self = circular;
const result = normalizeError(circular);
expect(result.message).toBe('Unknown error');
});
});

15
utils/error.ts Normal file
View File

@@ -0,0 +1,15 @@
export const normalizeError = (error: unknown): Error => {
if (error instanceof Error) {
return error;
}
if (typeof error === 'string') {
return new Error(error);
}
try {
return new Error(JSON.stringify(error));
} catch {
return new Error('Unknown error');
}
};