messages and reviews

This commit is contained in:
jackiettran
2025-07-17 00:16:01 -04:00
parent aa3adc58ca
commit 1dbe821e70
21 changed files with 1981 additions and 102 deletions

50
backend/models/Message.js Normal file
View File

@@ -0,0 +1,50 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const Message = sequelize.define('Message', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
senderId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
receiverId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
subject: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.TEXT,
allowNull: false
},
isRead: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
parentMessageId: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'Messages',
key: 'id'
}
}
}, {
timestamps: true
});
module.exports = Message;

View File

@@ -2,6 +2,7 @@ const sequelize = require('../config/database');
const User = require('./User');
const Item = require('./Item');
const Rental = require('./Rental');
const Message = require('./Message');
User.hasMany(Item, { as: 'ownedItems', foreignKey: 'ownerId' });
Item.belongsTo(User, { as: 'owner', foreignKey: 'ownerId' });
@@ -14,9 +15,17 @@ Rental.belongsTo(Item, { as: 'item', foreignKey: 'itemId' });
Rental.belongsTo(User, { as: 'renter', foreignKey: 'renterId' });
Rental.belongsTo(User, { as: 'owner', foreignKey: 'ownerId' });
User.hasMany(Message, { as: 'sentMessages', foreignKey: 'senderId' });
User.hasMany(Message, { as: 'receivedMessages', foreignKey: 'receiverId' });
Message.belongsTo(User, { as: 'sender', foreignKey: 'senderId' });
Message.belongsTo(User, { as: 'receiver', foreignKey: 'receiverId' });
Message.hasMany(Message, { as: 'replies', foreignKey: 'parentMessageId' });
Message.belongsTo(Message, { as: 'parentMessage', foreignKey: 'parentMessageId' });
module.exports = {
sequelize,
User,
Item,
Rental
Rental,
Message
};

169
backend/routes/messages.js Normal file
View File

@@ -0,0 +1,169 @@
const express = require('express');
const { Message, User } = require('../models');
const { authenticateToken } = require('../middleware/auth');
const router = express.Router();
// Get all messages for the current user (inbox)
router.get('/', authenticateToken, async (req, res) => {
try {
const messages = await Message.findAll({
where: { receiverId: req.user.id },
include: [
{
model: User,
as: 'sender',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
}
],
order: [['createdAt', 'DESC']]
});
res.json(messages);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get sent messages
router.get('/sent', authenticateToken, async (req, res) => {
try {
const messages = await Message.findAll({
where: { senderId: req.user.id },
include: [
{
model: User,
as: 'receiver',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
}
],
order: [['createdAt', 'DESC']]
});
res.json(messages);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get a single message with replies
router.get('/:id', authenticateToken, async (req, res) => {
try {
const message = await Message.findOne({
where: {
id: req.params.id,
[require('sequelize').Op.or]: [
{ senderId: req.user.id },
{ receiverId: req.user.id }
]
},
include: [
{
model: User,
as: 'sender',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
},
{
model: User,
as: 'receiver',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
},
{
model: Message,
as: 'replies',
include: [{
model: User,
as: 'sender',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
}]
}
]
});
if (!message) {
return res.status(404).json({ error: 'Message not found' });
}
// Mark as read if user is the receiver
if (message.receiverId === req.user.id && !message.isRead) {
await message.update({ isRead: true });
}
res.json(message);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Send a new message
router.post('/', authenticateToken, async (req, res) => {
try {
const { receiverId, subject, content, parentMessageId } = req.body;
// Check if receiver exists
const receiver = await User.findByPk(receiverId);
if (!receiver) {
return res.status(404).json({ error: 'Receiver not found' });
}
// Prevent sending messages to self
if (receiverId === req.user.id) {
return res.status(400).json({ error: 'Cannot send messages to yourself' });
}
const message = await Message.create({
senderId: req.user.id,
receiverId,
subject,
content,
parentMessageId
});
const messageWithSender = await Message.findByPk(message.id, {
include: [{
model: User,
as: 'sender',
attributes: ['id', 'firstName', 'lastName', 'profileImage']
}]
});
res.status(201).json(messageWithSender);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Mark message as read
router.put('/:id/read', authenticateToken, async (req, res) => {
try {
const message = await Message.findOne({
where: {
id: req.params.id,
receiverId: req.user.id
}
});
if (!message) {
return res.status(404).json({ error: 'Message not found' });
}
await message.update({ isRead: true });
res.json(message);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get unread message count
router.get('/unread/count', authenticateToken, async (req, res) => {
try {
const count = await Message.count({
where: {
receiverId: req.user.id,
isRead: false
}
});
res.json({ count });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;

View File

@@ -14,15 +14,32 @@ router.get('/profile', authenticateToken, async (req, res) => {
}
});
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' });
}
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
router.put('/profile', authenticateToken, async (req, res) => {
try {
const { firstName, lastName, phone, address } = req.body;
const { firstName, lastName, phone, address, profileImage } = req.body;
await req.user.update({
firstName,
lastName,
phone,
address
address,
profileImage
});
const updatedUser = await User.findByPk(req.user.id, {

View File

@@ -8,6 +8,7 @@ const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const itemRoutes = require('./routes/items');
const rentalRoutes = require('./routes/rentals');
const messageRoutes = require('./routes/messages');
const app = express();
@@ -19,6 +20,7 @@ app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/items', itemRoutes);
app.use('/api/rentals', rentalRoutes);
app.use('/api/messages', messageRoutes);
app.get('/', (req, res) => {
res.json({ message: 'Rentall API is running!' });