Initial commit - Rentall App
- Full-stack rental marketplace application - React frontend with TypeScript - Node.js/Express backend with JWT authentication - Features: item listings, rental requests, calendar availability, user profiles
This commit is contained in:
156
backend/routes/rentals.js
Normal file
156
backend/routes/rentals.js
Normal file
@@ -0,0 +1,156 @@
|
||||
const express = require('express');
|
||||
const { Op } = require('sequelize');
|
||||
const { Rental, Item, User } = require('../models'); // Import from models/index.js to get models with associations
|
||||
const { authenticateToken } = require('../middleware/auth');
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/my-rentals', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const rentals = await Rental.findAll({
|
||||
where: { renterId: req.user.id },
|
||||
include: [
|
||||
{ model: Item, as: 'item' },
|
||||
{ model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] }
|
||||
],
|
||||
order: [['createdAt', 'DESC']]
|
||||
});
|
||||
|
||||
res.json(rentals);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/my-listings', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const rentals = await Rental.findAll({
|
||||
where: { ownerId: req.user.id },
|
||||
include: [
|
||||
{ model: Item, as: 'item' },
|
||||
{ model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] }
|
||||
],
|
||||
order: [['createdAt', 'DESC']]
|
||||
});
|
||||
|
||||
res.json(rentals);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { itemId, startDate, endDate, deliveryMethod, deliveryAddress, notes } = req.body;
|
||||
|
||||
const item = await Item.findByPk(itemId);
|
||||
if (!item) {
|
||||
return res.status(404).json({ error: 'Item not found' });
|
||||
}
|
||||
|
||||
if (!item.availability) {
|
||||
return res.status(400).json({ error: 'Item is not available' });
|
||||
}
|
||||
|
||||
const overlappingRental = await Rental.findOne({
|
||||
where: {
|
||||
itemId,
|
||||
status: { [Op.in]: ['confirmed', 'active'] },
|
||||
[Op.or]: [
|
||||
{
|
||||
startDate: { [Op.between]: [startDate, endDate] }
|
||||
},
|
||||
{
|
||||
endDate: { [Op.between]: [startDate, endDate] }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
if (overlappingRental) {
|
||||
return res.status(400).json({ error: 'Item is already booked for these dates' });
|
||||
}
|
||||
|
||||
const rentalDays = Math.ceil((new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24));
|
||||
const totalAmount = rentalDays * (item.pricePerDay || 0);
|
||||
|
||||
const rental = await Rental.create({
|
||||
itemId,
|
||||
renterId: req.user.id,
|
||||
ownerId: item.ownerId,
|
||||
startDate,
|
||||
endDate,
|
||||
totalAmount,
|
||||
deliveryMethod,
|
||||
deliveryAddress,
|
||||
notes
|
||||
});
|
||||
|
||||
const rentalWithDetails = await Rental.findByPk(rental.id, {
|
||||
include: [
|
||||
{ model: Item, as: 'item' },
|
||||
{ model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] },
|
||||
{ model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] }
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json(rentalWithDetails);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:id/status', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { status } = req.body;
|
||||
const rental = await Rental.findByPk(req.params.id);
|
||||
|
||||
if (!rental) {
|
||||
return res.status(404).json({ error: 'Rental not found' });
|
||||
}
|
||||
|
||||
if (rental.ownerId !== req.user.id && rental.renterId !== req.user.id) {
|
||||
return res.status(403).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
await rental.update({ status });
|
||||
|
||||
const updatedRental = await Rental.findByPk(rental.id, {
|
||||
include: [
|
||||
{ model: Item, as: 'item' },
|
||||
{ model: User, as: 'owner', attributes: ['id', 'username', 'firstName', 'lastName'] },
|
||||
{ model: User, as: 'renter', attributes: ['id', 'username', 'firstName', 'lastName'] }
|
||||
]
|
||||
});
|
||||
|
||||
res.json(updatedRental);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:id/review', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { rating, review } = req.body;
|
||||
const rental = await Rental.findByPk(req.params.id);
|
||||
|
||||
if (!rental) {
|
||||
return res.status(404).json({ error: 'Rental not found' });
|
||||
}
|
||||
|
||||
if (rental.renterId !== req.user.id) {
|
||||
return res.status(403).json({ error: 'Only renters can leave reviews' });
|
||||
}
|
||||
|
||||
if (rental.status !== 'completed') {
|
||||
return res.status(400).json({ error: 'Can only review completed rentals' });
|
||||
}
|
||||
|
||||
await rental.update({ rating, review });
|
||||
|
||||
res.json(rental);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user