refactor(logging): replace console usage in auth flows
This commit is contained in:
39
App.tsx
39
App.tsx
@@ -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 />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
27
utils/__tests__/error.test.ts
Normal file
27
utils/__tests__/error.test.ts
Normal 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
15
utils/error.ts
Normal 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');
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user