const express = require('express'); const { User, UserAddress } = require('../models'); // Import from models/index.js to get models with associations const { authenticateToken } = require('../middleware/auth'); const { uploadProfileImage } = require('../middleware/upload'); const logger = require('../utils/logger'); const fs = require('fs').promises; const path = require('path'); const router = express.Router(); router.get('/profile', authenticateToken, async (req, res) => { try { const user = await User.findByPk(req.user.id, { attributes: { exclude: ['password'] } }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User profile fetched", { userId: req.user.id }); res.json(user); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User profile fetch failed", { error: error.message, stack: error.stack, userId: req.user.id }); res.status(500).json({ error: error.message }); } }); // Address routes (must come before /:id route) router.get('/addresses', authenticateToken, async (req, res) => { try { const addresses = await UserAddress.findAll({ where: { userId: req.user.id }, order: [['isPrimary', 'DESC'], ['createdAt', 'ASC']] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User addresses fetched", { userId: req.user.id, addressCount: addresses.length }); res.json(addresses); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User addresses fetch failed", { error: error.message, stack: error.stack, userId: req.user.id }); res.status(500).json({ error: error.message }); } }); router.post('/addresses', authenticateToken, async (req, res) => { try { const address = await UserAddress.create({ ...req.body, userId: req.user.id }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User address created", { userId: req.user.id, addressId: address.id }); res.status(201).json(address); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User address creation failed", { error: error.message, stack: error.stack, userId: req.user.id, addressData: logger.sanitize(req.body) }); res.status(500).json({ error: error.message }); } }); router.put('/addresses/:id', authenticateToken, async (req, res) => { try { const address = await UserAddress.findByPk(req.params.id); if (!address) { return res.status(404).json({ error: 'Address not found' }); } if (address.userId !== req.user.id) { return res.status(403).json({ error: 'Unauthorized' }); } await address.update(req.body); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User address updated", { userId: req.user.id, addressId: req.params.id }); res.json(address); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User address update failed", { error: error.message, stack: error.stack, userId: req.user.id, addressId: req.params.id }); res.status(500).json({ error: error.message }); } }); router.delete('/addresses/:id', authenticateToken, async (req, res) => { try { const address = await UserAddress.findByPk(req.params.id); if (!address) { return res.status(404).json({ error: 'Address not found' }); } if (address.userId !== req.user.id) { return res.status(403).json({ error: 'Unauthorized' }); } await address.destroy(); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User address deleted", { userId: req.user.id, addressId: req.params.id }); res.status(204).send(); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User address deletion failed", { error: error.message, stack: error.stack, userId: req.user.id, addressId: req.params.id }); res.status(500).json({ error: error.message }); } }); // User availability routes (must come before /:id route) router.get('/availability', authenticateToken, async (req, res) => { try { const user = await User.findByPk(req.user.id, { attributes: ['defaultAvailableAfter', 'defaultAvailableBefore', 'defaultSpecifyTimesPerDay', 'defaultWeeklyTimes'] }); res.json({ generalAvailableAfter: user.defaultAvailableAfter, generalAvailableBefore: user.defaultAvailableBefore, specifyTimesPerDay: user.defaultSpecifyTimesPerDay, weeklyTimes: user.defaultWeeklyTimes }); } catch (error) { res.status(500).json({ error: error.message }); } }); router.put('/availability', authenticateToken, async (req, res) => { try { const { generalAvailableAfter, generalAvailableBefore, specifyTimesPerDay, weeklyTimes } = req.body; await User.update({ defaultAvailableAfter: generalAvailableAfter, defaultAvailableBefore: generalAvailableBefore, defaultSpecifyTimesPerDay: specifyTimesPerDay, defaultWeeklyTimes: weeklyTimes }, { where: { id: req.user.id } }); res.json({ message: 'Availability updated successfully' }); } catch (error) { res.status(500).json({ error: error.message }); } }); router.get('/:id', async (req, res) => { try { const user = await User.findByPk(req.params.id, { attributes: { exclude: ['password', 'email', 'phone', 'address'] } }); if (!user) { return res.status(404).json({ error: 'User not found' }); } const reqLogger = logger.withRequestId(req.id); reqLogger.info("Public user profile fetched", { requestedUserId: req.params.id }); res.json(user); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Public user profile fetch failed", { error: error.message, stack: error.stack, requestedUserId: req.params.id }); res.status(500).json({ error: error.message }); } }); router.put('/profile', authenticateToken, async (req, res) => { try { const { firstName, lastName, email, phone, address1, address2, city, state, zipCode, country } = req.body; // Build update object, excluding empty email const updateData = { firstName, lastName, phone, address1, address2, city, state, zipCode, country }; // Only include email if it's not empty if (email && email.trim() !== '') { updateData.email = email; } await req.user.update(updateData); const updatedUser = await User.findByPk(req.user.id, { attributes: { exclude: ['password'] } }); res.json(updatedUser); } catch (error) { console.error('Profile update error:', error); res.status(500).json({ error: error.message, details: error.errors ? error.errors.map(e => ({ field: e.path, message: e.message })) : undefined }); } }); // Upload profile image endpoint router.post('/profile/image', authenticateToken, (req, res) => { uploadProfileImage(req, res, async (err) => { if (err) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Profile image upload error", { error: err.message, userId: req.user.id }); return res.status(400).json({ error: err.message }); } if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } try { // Delete old profile image if exists const user = await User.findByPk(req.user.id); if (user.profileImage) { const oldImagePath = path.join(__dirname, '../uploads/profiles', user.profileImage); try { await fs.unlink(oldImagePath); } catch (unlinkErr) { const reqLogger = logger.withRequestId(req.id); reqLogger.warn("Error deleting old profile image", { error: unlinkErr.message, userId: req.user.id, oldImagePath }); } } // Update user with new image filename await user.update({ profileImage: req.file.filename }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Profile image uploaded successfully", { userId: req.user.id, filename: req.file.filename }); res.json({ message: 'Profile image uploaded successfully', filename: req.file.filename, imageUrl: `/uploads/profiles/${req.file.filename}` }); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Profile image database update failed", { error: error.message, stack: error.stack, userId: req.user.id }); res.status(500).json({ error: 'Failed to update profile image' }); } }); }); module.exports = router;