const express = require('express'); const { Op } = require('sequelize'); const { ItemRequest, ItemRequestResponse, User, Item } = require('../models'); const { authenticateToken } = require('../middleware/auth'); const logger = require('../utils/logger'); const router = express.Router(); router.get('/', async (req, res) => { try { const { search, status = 'open', page = 1, limit = 20 } = req.query; const where = { status }; if (search) { where[Op.or] = [ { title: { [Op.iLike]: `%${search}%` } }, { description: { [Op.iLike]: `%${search}%` } } ]; } const offset = (page - 1) * limit; const { count, rows } = await ItemRequest.findAndCountAll({ where, include: [ { model: User, as: 'requester', attributes: ['id', 'username', 'firstName', 'lastName'] } ], limit: parseInt(limit), offset: parseInt(offset), order: [['createdAt', 'DESC']] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item requests fetched", { search, status, requestsCount: count, page: parseInt(page), limit: parseInt(limit) }); res.json({ requests: rows, totalPages: Math.ceil(count / limit), currentPage: parseInt(page), totalRequests: count }); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item requests fetch failed", { error: error.message, stack: error.stack, query: req.query }); res.status(500).json({ error: error.message }); } }); router.get('/my-requests', authenticateToken, async (req, res) => { try { const requests = await ItemRequest.findAll({ where: { requesterId: req.user.id }, include: [ { model: User, as: 'requester', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: ItemRequestResponse, as: 'responses', include: [ { model: User, as: 'responder', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: Item, as: 'existingItem' } ] } ], order: [['createdAt', 'DESC']] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("User item requests fetched", { userId: req.user.id, requestsCount: requests.length }); res.json(requests); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("User item requests fetch failed", { error: error.message, stack: error.stack, userId: req.user.id }); res.status(500).json({ error: error.message }); } }); router.get('/:id', async (req, res) => { try { const request = await ItemRequest.findByPk(req.params.id, { include: [ { model: User, as: 'requester', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: ItemRequestResponse, as: 'responses', include: [ { model: User, as: 'responder', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: Item, as: 'existingItem' } ] } ] }); if (!request) { return res.status(404).json({ error: 'Item request not found' }); } const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request fetched", { requestId: req.params.id, requesterId: request.requesterId }); res.json(request); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request fetch failed", { error: error.message, stack: error.stack, requestId: req.params.id }); res.status(500).json({ error: error.message }); } }); router.post('/', authenticateToken, async (req, res) => { try { const request = await ItemRequest.create({ ...req.body, requesterId: req.user.id }); const requestWithRequester = await ItemRequest.findByPk(request.id, { include: [ { model: User, as: 'requester', attributes: ['id', 'username', 'firstName', 'lastName'] } ] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request created", { requestId: request.id, requesterId: req.user.id, title: req.body.title }); res.status(201).json(requestWithRequester); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request creation failed", { error: error.message, stack: error.stack, requesterId: req.user.id, requestData: logger.sanitize(req.body) }); res.status(500).json({ error: error.message }); } }); router.put('/:id', authenticateToken, async (req, res) => { try { const request = await ItemRequest.findByPk(req.params.id); if (!request) { return res.status(404).json({ error: 'Item request not found' }); } if (request.requesterId !== req.user.id) { return res.status(403).json({ error: 'Unauthorized' }); } await request.update(req.body); const updatedRequest = await ItemRequest.findByPk(request.id, { include: [ { model: User, as: 'requester', attributes: ['id', 'username', 'firstName', 'lastName'] } ] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request updated", { requestId: req.params.id, requesterId: req.user.id }); res.json(updatedRequest); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request update failed", { error: error.message, stack: error.stack, requestId: req.params.id, requesterId: req.user.id }); res.status(500).json({ error: error.message }); } }); router.delete('/:id', authenticateToken, async (req, res) => { try { const request = await ItemRequest.findByPk(req.params.id); if (!request) { return res.status(404).json({ error: 'Item request not found' }); } if (request.requesterId !== req.user.id) { return res.status(403).json({ error: 'Unauthorized' }); } await request.destroy(); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request deleted", { requestId: req.params.id, requesterId: req.user.id }); res.status(204).send(); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request deletion failed", { error: error.message, stack: error.stack, requestId: req.params.id, requesterId: req.user.id }); res.status(500).json({ error: error.message }); } }); router.post('/:id/responses', authenticateToken, async (req, res) => { try { const request = await ItemRequest.findByPk(req.params.id); if (!request) { return res.status(404).json({ error: 'Item request not found' }); } if (request.requesterId === req.user.id) { return res.status(400).json({ error: 'Cannot respond to your own request' }); } if (request.status !== 'open') { return res.status(400).json({ error: 'Cannot respond to closed request' }); } const response = await ItemRequestResponse.create({ ...req.body, itemRequestId: req.params.id, responderId: req.user.id }); await request.increment('responseCount'); const responseWithDetails = await ItemRequestResponse.findByPk(response.id, { include: [ { model: User, as: 'responder', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: Item, as: 'existingItem' } ] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request response created", { requestId: req.params.id, responseId: response.id, responderId: req.user.id }); res.status(201).json(responseWithDetails); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request response creation failed", { error: error.message, stack: error.stack, requestId: req.params.id, responderId: req.user.id }); res.status(500).json({ error: error.message }); } }); router.put('/responses/:responseId/status', authenticateToken, async (req, res) => { try { const { status } = req.body; const response = await ItemRequestResponse.findByPk(req.params.responseId, { include: [ { model: ItemRequest, as: 'itemRequest' } ] }); if (!response) { return res.status(404).json({ error: 'Response not found' }); } if (response.itemRequest.requesterId !== req.user.id) { return res.status(403).json({ error: 'Only the requester can update response status' }); } await response.update({ status }); if (status === 'accepted') { await response.itemRequest.update({ status: 'fulfilled' }); } const updatedResponse = await ItemRequestResponse.findByPk(response.id, { include: [ { model: User, as: 'responder', attributes: ['id', 'username', 'firstName', 'lastName'] }, { model: Item, as: 'existingItem' } ] }); const reqLogger = logger.withRequestId(req.id); reqLogger.info("Item request response status updated", { responseId: req.params.responseId, newStatus: status, requesterId: req.user.id, requestFulfilled: status === 'accepted' }); res.json(updatedResponse); } catch (error) { const reqLogger = logger.withRequestId(req.id); reqLogger.error("Item request response status update failed", { error: error.message, stack: error.stack, responseId: req.params.responseId, requesterId: req.user.id }); res.status(500).json({ error: error.message }); } }); module.exports = router;