large mock refector hopefully done + no errors?
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 1h17m3s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 1h17m3s
This commit is contained in:
@@ -157,17 +157,17 @@ router.post('/upload-and-process', optionalAuth, uploadToDisk.single('flyerFile'
|
||||
return res.status(409).json({ message: 'This flyer has already been processed.', flyerId: existingFlyer.flyer_id });
|
||||
}
|
||||
|
||||
const user = req.user as UserProfile | undefined;
|
||||
const userProfile = req.user as UserProfile | undefined;
|
||||
// Construct a user address string from their profile if they are logged in.
|
||||
let userProfileAddress: string | undefined = undefined;
|
||||
if (user?.address) {
|
||||
if (userProfile?.address) {
|
||||
userProfileAddress = [
|
||||
user.address.address_line_1,
|
||||
user.address.address_line_2,
|
||||
user.address.city,
|
||||
user.address.province_state,
|
||||
user.address.postal_code,
|
||||
user.address.country
|
||||
userProfile.address.address_line_1,
|
||||
userProfile.address.address_line_2,
|
||||
userProfile.address.city,
|
||||
userProfile.address.province_state,
|
||||
userProfile.address.postal_code,
|
||||
userProfile.address.country
|
||||
].filter(Boolean).join(', ');
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ router.post('/upload-and-process', optionalAuth, uploadToDisk.single('flyerFile'
|
||||
filePath: req.file.path,
|
||||
originalFileName: req.file.originalname,
|
||||
checksum: checksum,
|
||||
userId: user?.user_id,
|
||||
userId: userProfile?.user_id,
|
||||
submitterIp: req.ip, // Capture the submitter's IP address
|
||||
userProfileAddress: userProfileAddress, // Pass the user's profile address
|
||||
});
|
||||
@@ -286,7 +286,7 @@ router.post('/flyers/process', optionalAuth, uploadToDisk.single('flyerImage'),
|
||||
// Pull common metadata fields (checksum, originalFileName) from whichever shape we parsed.
|
||||
const checksum = parsed.checksum ?? parsed?.data?.checksum ?? '';
|
||||
const originalFileName = parsed.originalFileName ?? parsed?.data?.originalFileName ?? req.file.originalname;
|
||||
const user = req.user as UserProfile | undefined;
|
||||
const userProfile = req.user as UserProfile | undefined;
|
||||
|
||||
// Validate extractedData to avoid database errors (e.g., null store_name)
|
||||
if (!extractedData || typeof extractedData !== 'object') {
|
||||
@@ -333,7 +333,7 @@ router.post('/flyers/process', optionalAuth, uploadToDisk.single('flyerImage'),
|
||||
valid_to: extractedData.valid_to ?? null,
|
||||
store_address: extractedData.store_address ?? null,
|
||||
item_count: 0, // Set default to 0; the trigger will update it.
|
||||
uploaded_by: user?.user_id, // Associate with user if logged in
|
||||
uploaded_by: userProfile?.user_id, // Associate with user if logged in
|
||||
};
|
||||
|
||||
// 3. Create flyer and its items in a transaction
|
||||
@@ -343,7 +343,7 @@ router.post('/flyers/process', optionalAuth, uploadToDisk.single('flyerImage'),
|
||||
|
||||
// Log this significant event
|
||||
await db.adminRepo.logActivity({
|
||||
userId: user?.user_id,
|
||||
userId: userProfile?.user_id,
|
||||
action: 'flyer_processed',
|
||||
displayText: `Processed a new flyer for ${flyerData.store_name}.`,
|
||||
details: { flyerId: newFlyer.flyer_id, storeName: flyerData.store_name }
|
||||
|
||||
@@ -285,7 +285,14 @@ describe('Auth Routes (/api/auth)', () => {
|
||||
|
||||
// Assert
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.user).toEqual({ user_id: 'user-123', email: loginCredentials.email });
|
||||
// The API now returns a nested UserProfile object
|
||||
expect(response.body.user).toEqual(expect.objectContaining({
|
||||
user_id: 'user-123',
|
||||
user: expect.objectContaining({
|
||||
user_id: 'user-123',
|
||||
email: loginCredentials.email
|
||||
})
|
||||
}));
|
||||
expect(response.body.token).toBeTypeOf('string');
|
||||
expect(response.headers['set-cookie']).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -167,14 +167,14 @@ router.post('/login', (req: Request, res: Response, next: NextFunction) => {
|
||||
return res.status(401).json({ message: info.message || 'Login failed' });
|
||||
}
|
||||
|
||||
const typedUser = user as UserProfile;
|
||||
const payload = { user_id: typedUser.user_id, email: typedUser.user.email, role: typedUser.role };
|
||||
const userProfile = user as UserProfile;
|
||||
const payload = { user_id: userProfile.user_id, email: userProfile.user.email, role: userProfile.role };
|
||||
const accessToken = jwt.sign(payload, JWT_SECRET, { expiresIn: '15m' });
|
||||
|
||||
try {
|
||||
const refreshToken = crypto.randomBytes(64).toString('hex'); // This was a duplicate, fixed.
|
||||
await userRepo.saveRefreshToken(typedUser.user_id, refreshToken, req.log);
|
||||
req.log.info(`JWT and refresh token issued for user: ${typedUser.user.email}`);
|
||||
await userRepo.saveRefreshToken(userProfile.user_id, refreshToken, req.log);
|
||||
req.log.info(`JWT and refresh token issued for user: ${userProfile.user.email}`);
|
||||
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
@@ -184,9 +184,9 @@ router.post('/login', (req: Request, res: Response, next: NextFunction) => {
|
||||
|
||||
res.cookie('refreshToken', refreshToken, cookieOptions);
|
||||
// Return the full user profile object on login to avoid a second fetch on the client.
|
||||
return res.json({ user: typedUser, token: accessToken });
|
||||
return res.json({ user: userProfile, token: accessToken });
|
||||
} catch (tokenErr) {
|
||||
req.log.error({ error: tokenErr }, `Failed to save refresh token during login for user: ${typedUser.user.email}`);
|
||||
req.log.error({ error: tokenErr }, `Failed to save refresh token during login for user: ${userProfile.user.email}`);
|
||||
return next(tokenErr);
|
||||
}
|
||||
})(req, res, next);
|
||||
|
||||
@@ -125,7 +125,18 @@ describe('Passport Configuration', () => {
|
||||
expect(mockedDb.userRepo.findUserWithProfileByEmail).toHaveBeenCalledWith('test@test.com', logger);
|
||||
expect(bcrypt.compare).toHaveBeenCalledWith('password', 'hashed_password');
|
||||
expect(mockedDb.adminRepo.resetFailedLoginAttempts).toHaveBeenCalledWith('user-123', '127.0.0.1', logger);
|
||||
expect(done).toHaveBeenCalledWith(null, mockUser);
|
||||
// The strategy transforms the flat DB user into a nested UserProfile structure.
|
||||
// We need to construct the expected object based on that transformation logic.
|
||||
const { password_hash, email, ...profileData } = mockUser;
|
||||
const expectedUserProfile = {
|
||||
...profileData,
|
||||
user: {
|
||||
user_id: mockUser.user_id,
|
||||
email: mockUser.email,
|
||||
}
|
||||
};
|
||||
|
||||
expect(done).toHaveBeenCalledWith(null, expectedUserProfile);
|
||||
});
|
||||
|
||||
it('should call done(null, false) if user is not found', async () => {
|
||||
|
||||
@@ -122,9 +122,9 @@ router.post(
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try { // The try-catch block was already correct here.
|
||||
if (!req.file) return res.status(400).json({ message: 'No avatar file uploaded.' });
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
const avatarUrl = `/uploads/avatars/${req.file.filename}`;
|
||||
const updatedProfile = await db.userRepo.updateUserProfile(user.user_id, { avatar_url: avatarUrl }, req.log);
|
||||
const updatedProfile = await db.userRepo.updateUserProfile(userProfile.user_id, { avatar_url: avatarUrl }, req.log);
|
||||
res.json(updatedProfile);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -141,15 +141,15 @@ router.get(
|
||||
'/notifications',
|
||||
validateRequest(notificationQuerySchema),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
// Fix: Cast to UserProfile and access the nested user property.
|
||||
const user = req.user as UserProfile;
|
||||
// Cast to UserProfile to access user properties safely.
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
try {
|
||||
const { query } = req as unknown as GetNotificationsRequest;
|
||||
// Explicitly convert to numbers to ensure the repo receives correct types
|
||||
const limit = query.limit ? Number(query.limit) : 20;
|
||||
const offset = query.offset ? Number(query.offset) : 0;
|
||||
const notifications = await db.notificationRepo.getNotificationsForUser(user.user_id, limit, offset, req.log);
|
||||
const notifications = await db.notificationRepo.getNotificationsForUser(userProfile.user_id, limit, offset, req.log);
|
||||
res.json(notifications);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -164,10 +164,9 @@ router.post(
|
||||
'/notifications/mark-all-read',
|
||||
validateRequest(emptySchema),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try { // The try-catch block was already correct here.
|
||||
// Fix: Cast to UserProfile and access the nested user property.
|
||||
const user = req.user as UserProfile;
|
||||
await db.notificationRepo.markAllNotificationsAsRead(user.user_id, req.log);
|
||||
try {
|
||||
const userProfile = req.user as UserProfile;
|
||||
await db.notificationRepo.markAllNotificationsAsRead(userProfile.user_id, req.log);
|
||||
res.status(204).send(); // No Content
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -183,12 +182,11 @@ type MarkNotificationReadRequest = z.infer<typeof notificationIdSchema>;
|
||||
router.post(
|
||||
'/notifications/:notificationId/mark-read', validateRequest(notificationIdSchema),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try { // The try-catch block was already correct here.
|
||||
// Fix: Cast to UserProfile and access the nested user property.
|
||||
const user = req.user as UserProfile;
|
||||
try {
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as MarkNotificationReadRequest;
|
||||
await db.notificationRepo.markNotificationAsRead(params.notificationId, user.user_id, req.log);
|
||||
await db.notificationRepo.markNotificationAsRead(params.notificationId, userProfile.user_id, req.log);
|
||||
res.status(204).send(); // Success, no content to return
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -201,11 +199,11 @@ router.post(
|
||||
*/
|
||||
router.get('/profile', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/profile - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
try {
|
||||
logger.debug(`[ROUTE] Calling db.userRepo.findUserProfileById for user: ${user.user_id}`);
|
||||
const userProfile = await db.userRepo.findUserProfileById(user.user_id, req.log);
|
||||
res.json(userProfile);
|
||||
logger.debug(`[ROUTE] Calling db.userRepo.findUserProfileById for user: ${userProfile.user_id}`);
|
||||
const fullUserProfile = await db.userRepo.findUserProfileById(userProfile.user_id, req.log);
|
||||
res.json(fullUserProfile);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] GET /api/users/profile - ERROR`);
|
||||
next(error);
|
||||
@@ -218,11 +216,11 @@ router.get('/profile', validateRequest(emptySchema), async (req, res, next: Next
|
||||
type UpdateProfileRequest = z.infer<typeof updateProfileSchema>;
|
||||
router.put('/profile', validateRequest(updateProfileSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/profile - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as UpdateProfileRequest;
|
||||
try {
|
||||
const updatedProfile = await db.userRepo.updateUserProfile(user.user_id, body, req.log);
|
||||
const updatedProfile = await db.userRepo.updateUserProfile(userProfile.user_id, body, req.log);
|
||||
res.json(updatedProfile);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] PUT /api/users/profile - ERROR`);
|
||||
@@ -236,14 +234,14 @@ router.put('/profile', validateRequest(updateProfileSchema), async (req, res, ne
|
||||
type UpdatePasswordRequest = z.infer<typeof updatePasswordSchema>;
|
||||
router.put('/profile/password', validateRequest(updatePasswordSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/profile/password - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as UpdatePasswordRequest;
|
||||
|
||||
try {
|
||||
const saltRounds = 10;
|
||||
const hashedPassword = await bcrypt.hash(body.newPassword, saltRounds);
|
||||
await db.userRepo.updateUserPassword(user.user_id, hashedPassword, req.log);
|
||||
await db.userRepo.updateUserPassword(userProfile.user_id, hashedPassword, req.log);
|
||||
res.status(200).json({ message: 'Password updated successfully.' });
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] PUT /api/users/profile/password - ERROR`);
|
||||
@@ -257,12 +255,12 @@ router.put('/profile/password', validateRequest(updatePasswordSchema), async (re
|
||||
type DeleteAccountRequest = z.infer<typeof deleteAccountSchema>;
|
||||
router.delete('/account', validateRequest(deleteAccountSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] DELETE /api/users/account - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as DeleteAccountRequest;
|
||||
|
||||
try {
|
||||
const userWithHash = await db.userRepo.findUserWithPasswordHashById(user.user_id, req.log);
|
||||
const userWithHash = await db.userRepo.findUserWithPasswordHashById(userProfile.user_id, req.log);
|
||||
if (!userWithHash || !userWithHash.password_hash) {
|
||||
return res.status(404).json({ message: 'User not found or password not set.' });
|
||||
}
|
||||
@@ -272,7 +270,7 @@ router.delete('/account', validateRequest(deleteAccountSchema), async (req, res,
|
||||
return res.status(403).json({ message: 'Incorrect password.' });
|
||||
}
|
||||
|
||||
await db.userRepo.deleteUserById(user.user_id, req.log);
|
||||
await db.userRepo.deleteUserById(userProfile.user_id, req.log);
|
||||
res.status(200).json({ message: 'Account deleted successfully.' });
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] DELETE /api/users/account - ERROR`);
|
||||
@@ -285,9 +283,9 @@ router.delete('/account', validateRequest(deleteAccountSchema), async (req, res,
|
||||
*/
|
||||
router.get('/watched-items', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/watched-items - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
try {
|
||||
const items = await db.personalizationRepo.getWatchedItems(user.user_id, req.log);
|
||||
const items = await db.personalizationRepo.getWatchedItems(userProfile.user_id, req.log);
|
||||
res.json(items);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] GET /api/users/watched-items - ERROR`);
|
||||
@@ -301,11 +299,11 @@ router.get('/watched-items', validateRequest(emptySchema), async (req, res, next
|
||||
type AddWatchedItemRequest = z.infer<typeof addWatchedItemSchema>;
|
||||
router.post('/watched-items', validateRequest(addWatchedItemSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] POST /api/users/watched-items - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as AddWatchedItemRequest;
|
||||
try {
|
||||
const newItem = await db.personalizationRepo.addWatchedItem(user.user_id, body.itemName, body.category, req.log);
|
||||
const newItem = await db.personalizationRepo.addWatchedItem(userProfile.user_id, body.itemName, body.category, req.log);
|
||||
res.status(201).json(newItem);
|
||||
} catch (error) {
|
||||
if (error instanceof ForeignKeyConstraintError) {
|
||||
@@ -327,11 +325,11 @@ const watchedItemIdSchema = numericIdParam('masterItemId');
|
||||
type DeleteWatchedItemRequest = z.infer<typeof watchedItemIdSchema>;
|
||||
router.delete('/watched-items/:masterItemId', validateRequest(watchedItemIdSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] DELETE /api/users/watched-items/:masterItemId - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as DeleteWatchedItemRequest;
|
||||
try {
|
||||
await db.personalizationRepo.removeWatchedItem(user.user_id, params.masterItemId, req.log);
|
||||
await db.personalizationRepo.removeWatchedItem(userProfile.user_id, params.masterItemId, req.log);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] DELETE /api/users/watched-items/:masterItemId - ERROR`);
|
||||
@@ -344,9 +342,9 @@ router.delete('/watched-items/:masterItemId', validateRequest(watchedItemIdSchem
|
||||
*/
|
||||
router.get('/shopping-lists', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/shopping-lists - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
try {
|
||||
const lists = await db.shoppingRepo.getShoppingLists(user.user_id, req.log);
|
||||
const lists = await db.shoppingRepo.getShoppingLists(userProfile.user_id, req.log);
|
||||
res.json(lists);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] GET /api/users/shopping-lists - ERROR`);
|
||||
@@ -361,10 +359,10 @@ const shoppingListIdSchema = numericIdParam('listId');
|
||||
type GetShoppingListRequest = z.infer<typeof shoppingListIdSchema>;
|
||||
router.get('/shopping-lists/:listId', validateRequest(shoppingListIdSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/shopping-lists/:listId - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
const { params } = req as unknown as GetShoppingListRequest;
|
||||
try {
|
||||
const list = await db.shoppingRepo.getShoppingListById(params.listId, user.user_id, req.log);
|
||||
const list = await db.shoppingRepo.getShoppingListById(params.listId, userProfile.user_id, req.log);
|
||||
res.json(list);
|
||||
} catch (error) {
|
||||
logger.error({ error, listId: params.listId }, `[ROUTE] GET /api/users/shopping-lists/:listId - ERROR`);
|
||||
@@ -378,11 +376,11 @@ router.get('/shopping-lists/:listId', validateRequest(shoppingListIdSchema), asy
|
||||
type CreateShoppingListRequest = z.infer<typeof createShoppingListSchema>;
|
||||
router.post('/shopping-lists', validateRequest(createShoppingListSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] POST /api/users/shopping-lists - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as CreateShoppingListRequest;
|
||||
try {
|
||||
const newList = await db.shoppingRepo.createShoppingList(user.user_id, body.name, req.log);
|
||||
const newList = await db.shoppingRepo.createShoppingList(userProfile.user_id, body.name, req.log);
|
||||
res.status(201).json(newList);
|
||||
} catch (error) {
|
||||
if (error instanceof ForeignKeyConstraintError) {
|
||||
@@ -402,11 +400,11 @@ router.post('/shopping-lists', validateRequest(createShoppingListSchema), async
|
||||
*/
|
||||
router.delete('/shopping-lists/:listId', validateRequest(shoppingListIdSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] DELETE /api/users/shopping-lists/:listId - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as GetShoppingListRequest;
|
||||
try {
|
||||
await db.shoppingRepo.deleteShoppingList(params.listId, user.user_id, req.log);
|
||||
await db.shoppingRepo.deleteShoppingList(params.listId, userProfile.user_id, req.log);
|
||||
res.status(204).send();
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
||||
@@ -495,11 +493,11 @@ const updatePreferencesSchema = z.object({
|
||||
type UpdatePreferencesRequest = z.infer<typeof updatePreferencesSchema>;
|
||||
router.put('/profile/preferences', validateRequest(updatePreferencesSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/profile/preferences - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as UpdatePreferencesRequest;
|
||||
try {
|
||||
const updatedProfile = await db.userRepo.updateUserPreferences(user.user_id, body, req.log);
|
||||
const updatedProfile = await db.userRepo.updateUserPreferences(userProfile.user_id, body, req.log);
|
||||
res.json(updatedProfile);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] PUT /api/users/profile/preferences - ERROR`);
|
||||
@@ -509,9 +507,9 @@ router.put('/profile/preferences', validateRequest(updatePreferencesSchema), asy
|
||||
|
||||
router.get('/me/dietary-restrictions', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/me/dietary-restrictions - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
try {
|
||||
const restrictions = await db.personalizationRepo.getUserDietaryRestrictions(user.user_id, req.log);
|
||||
const restrictions = await db.personalizationRepo.getUserDietaryRestrictions(userProfile.user_id, req.log);
|
||||
res.json(restrictions);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] GET /api/users/me/dietary-restrictions - ERROR`);
|
||||
@@ -525,11 +523,11 @@ const setUserRestrictionsSchema = z.object({
|
||||
type SetUserRestrictionsRequest = z.infer<typeof setUserRestrictionsSchema>;
|
||||
router.put('/me/dietary-restrictions', validateRequest(setUserRestrictionsSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/me/dietary-restrictions - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as SetUserRestrictionsRequest;
|
||||
try {
|
||||
await db.personalizationRepo.setUserDietaryRestrictions(user.user_id, body.restrictionIds, req.log);
|
||||
await db.personalizationRepo.setUserDietaryRestrictions(userProfile.user_id, body.restrictionIds, req.log);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
if (error instanceof ForeignKeyConstraintError) {
|
||||
@@ -546,9 +544,9 @@ router.put('/me/dietary-restrictions', validateRequest(setUserRestrictionsSchema
|
||||
|
||||
router.get('/me/appliances', validateRequest(emptySchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] GET /api/users/me/appliances - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
try {
|
||||
const appliances = await db.personalizationRepo.getUserAppliances(user.user_id, req.log);
|
||||
const appliances = await db.personalizationRepo.getUserAppliances(userProfile.user_id, req.log);
|
||||
res.json(appliances);
|
||||
} catch (error) {
|
||||
logger.error({ error }, `[ROUTE] GET /api/users/me/appliances - ERROR`);
|
||||
@@ -562,11 +560,11 @@ const setUserAppliancesSchema = z.object({
|
||||
type SetUserAppliancesRequest = z.infer<typeof setUserAppliancesSchema>;
|
||||
router.put('/me/appliances', validateRequest(setUserAppliancesSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/me/appliances - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body } = req as unknown as SetUserAppliancesRequest;
|
||||
try {
|
||||
await db.personalizationRepo.setUserAppliances(user.user_id, body.applianceIds, req.log);
|
||||
await db.personalizationRepo.setUserAppliances(userProfile.user_id, body.applianceIds, req.log);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
if (error instanceof ForeignKeyConstraintError) {
|
||||
@@ -588,13 +586,13 @@ router.put('/me/appliances', validateRequest(setUserAppliancesSchema), async (re
|
||||
const addressIdSchema = numericIdParam('addressId');
|
||||
type GetAddressRequest = z.infer<typeof addressIdSchema>;
|
||||
router.get('/addresses/:addressId', validateRequest(addressIdSchema), async (req, res, next: NextFunction) => {
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as GetAddressRequest;
|
||||
try {
|
||||
const addressId = params.addressId;
|
||||
// Security check: Ensure the requested addressId matches the one on the user's profile.
|
||||
if (user.address_id !== addressId) {
|
||||
if (userProfile.address_id !== addressId) {
|
||||
return res.status(403).json({ message: 'Forbidden: You can only access your own address.' });
|
||||
}
|
||||
const address = await db.addressRepo.getAddressById(addressId, req.log); // This will throw NotFoundError if not found
|
||||
@@ -619,7 +617,7 @@ const updateUserAddressSchema = z.object({
|
||||
});
|
||||
type UpdateUserAddressRequest = z.infer<typeof updateUserAddressSchema>;
|
||||
router.put('/profile/address', validateRequest(updateUserAddressSchema), async (req, res, next: NextFunction) => {
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { body: addressData } = req as unknown as UpdateUserAddressRequest;
|
||||
|
||||
@@ -627,7 +625,7 @@ router.put('/profile/address', validateRequest(updateUserAddressSchema), async (
|
||||
// Per ADR-002, complex operations involving multiple database writes should be
|
||||
// encapsulated in a single service method that manages the transaction.
|
||||
// This ensures both the address upsert and the user profile update are atomic.
|
||||
const addressId = await userService.upsertUserAddress(user, addressData, req.log); // This was a duplicate, fixed.
|
||||
const addressId = await userService.upsertUserAddress(userProfile, addressData, req.log); // This was a duplicate, fixed.
|
||||
res.status(200).json({ message: 'Address updated successfully', address_id: addressId });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -641,11 +639,11 @@ const recipeIdSchema = numericIdParam('recipeId');
|
||||
type DeleteRecipeRequest = z.infer<typeof recipeIdSchema>;
|
||||
router.delete('/recipes/:recipeId', validateRequest(recipeIdSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] DELETE /api/users/recipes/:recipeId - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as DeleteRecipeRequest;
|
||||
try {
|
||||
await db.recipeRepo.deleteRecipe(params.recipeId, user.user_id, false, req.log);
|
||||
await db.recipeRepo.deleteRecipe(params.recipeId, userProfile.user_id, false, req.log);
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
logger.error({ error, params: req.params }, `[ROUTE] DELETE /api/users/recipes/:recipeId - ERROR`);
|
||||
@@ -670,12 +668,12 @@ const updateRecipeSchema = recipeIdSchema.extend({
|
||||
type UpdateRecipeRequest = z.infer<typeof updateRecipeSchema>;
|
||||
router.put('/recipes/:recipeId', validateRequest(updateRecipeSchema), async (req, res, next: NextFunction) => {
|
||||
logger.debug(`[ROUTE] PUT /api/users/recipes/:recipeId - ENTER`);
|
||||
const user = req.user as UserProfile;
|
||||
const userProfile = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params, body } = req as unknown as UpdateRecipeRequest;
|
||||
|
||||
try {
|
||||
const updatedRecipe = await db.recipeRepo.updateRecipe(params.recipeId, user.user_id, body, req.log);
|
||||
const updatedRecipe = await db.recipeRepo.updateRecipe(params.recipeId, userProfile.user_id, body, req.log);
|
||||
res.json(updatedRecipe);
|
||||
} catch (error) {
|
||||
logger.error({ error, params: req.params, body: req.body }, `[ROUTE] PUT /api/users/recipes/:recipeId - ERROR`);
|
||||
|
||||
Reference in New Issue
Block a user