diff --git a/middleware/authenticate.js b/middleware/authenticate.js index 354cbe3..5fc4571 100644 --- a/middleware/authenticate.js +++ b/middleware/authenticate.js @@ -6,4 +6,5 @@ function authenticate(req, res, next) { req.userId = decoded.userId; next(); }); -} \ No newline at end of file +} + diff --git a/models/DirectMessage.js b/models/DirectMessage.js new file mode 100644 index 0000000..ffd8347 --- /dev/null +++ b/models/DirectMessage.js @@ -0,0 +1,27 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const DirectMessageSchema = new Schema({ + sender: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true + }, + recipient: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true + }, + content: { + type: String, + required: true + }, + timestamp: { + type: Date, + default: Date.now + }, +}, { + timestamps: true +}); + +module.exports = mongoose.model('DirectMessage', DirectMessageSchema); diff --git a/models/Swit.js b/models/Swit.js index 366ac12..292230b 100644 --- a/models/Swit.js +++ b/models/Swit.js @@ -9,7 +9,8 @@ const SwitSchema = new mongoose.Schema({ text: { type: String }, user: { type: mongoose.Schema.Types.ObjectId, ref: 'User'}, date: { type: Date, default: Date.now}, - username: String + username: String, + likes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // Add this line }], reposts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User'}] }, { timestamps: true }); diff --git a/routes/dm.js b/routes/dm.js new file mode 100644 index 0000000..afe8a75 --- /dev/null +++ b/routes/dm.js @@ -0,0 +1,140 @@ +const express = require('express'); +const router = express.Router(); +const DirectMessage = require('../models/DirectMessage'); +const jwt = require('jsonwebtoken'); +const User = require('../models/User'); + +function authenticate(req, res, next) { + const token = req.header('Authorization'); + if (!token) return res.sendStatus(401); + jwt.verify(token, 'SECRET_KEY', (err, decoded) => { + if (err) return res.sendStatus(401); + req.userId = decoded.userId; + next(); + }); +} + +router.get('/', authenticate, async (req, res) => { + try { + const dms = await DirectMessage.find({ + $or: [ + { sender: req.userId }, + { recipient: req.userId }, + ], + }).populate('sender recipient', 'username'); + + // Group the messages by conversation + let conversations = {}; + dms.forEach(dm => { + const otherUserId = dm.sender._id.toString() === req.userId ? dm.recipient._id.toString() : dm.sender._id.toString(); + if (!conversations[otherUserId]) { + conversations[otherUserId] = { + userId: otherUserId, + username: dm.sender._id.toString() === req.userId ? dm.recipient.username : dm.sender.username, + messages: [], + }; + } + conversations[otherUserId].messages.push(dm); + }); + + // Convert the conversations object to an array + const conversationsArray = Object.values(conversations); + + res.json(conversationsArray); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Could not get Direct Messages." }); + } +}); + + +router.get('/:otherUserId', authenticate, async (req, res) => { + const { otherUserId } = req.params; + + try { + const dms = await DirectMessage.find({ + $or: [ + { sender: req.userId, recipient: otherUserId }, + { sender: otherUserId, recipient: req.userId }, + ], + }).populate('sender recipient', 'username'); + + res.json(dms); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Could not get Direct Messages." }); + } +}); + +router.delete('/:dmId', authenticate, async (req, res) => { + const { dmId } = req.params; + + try { + const dm = await DirectMessage.findById(dmId); + + if (!dm) { + return res.status(404).json({ error: "Direct Message not found." }); + } + + // Check if the authenticated user is the sender of the DM + if (dm.sender.toString() !== req.userId) { + return res.status(403).json({ error: "You can only delete your own Direct Messages." }); + } + + await dm.remove(); + + res.json({ message: "Direct Message deleted." }); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Could not delete Direct Message." }); + } +}); + +router.post('/send', authenticate, async (req, res) => { + const { recipientId, text, senderId } = req.body; + console.log(req.user) + + if (!recipientId || !text) { + return res.status(400).json({ error: "Recipient ID and text are required." }); + } + + try { + // Check if the recipient exists + const recipient = await User.findById(recipientId); + if (!recipient) { + return res.status(404).json({ error: "Recipient not found." }); + } +// Create the Direct Message +const dm = new DirectMessage({ + sender: senderId, + recipient: recipientId, + content: text, + }); + + // Save the Direct Message + const savedDm = await dm.save(); + + // Populate the sender and recipient fields with full user objects + await savedDm.populate('sender recipient', 'username').exec; + + // Now emit the `new_dm` event with the populated Direct Message + const io = req.app.io; + const senderSocketId = req.app.onlineUsers[senderId]; + const recipientSocketId = req.app.onlineUsers[recipientId]; + if (senderSocketId) { + io.to(senderSocketId).emit('new_dm', savedDm); + } + if (recipientSocketId) { + io.to(recipientSocketId).emit('new_dm', savedDm); + } + + + + res.json(savedDm); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Could not send Direct Message." }); + } + }); + +module.exports = router; diff --git a/routes/swit.js b/routes/swit.js index 63b05d2..83e9a19 100644 --- a/routes/swit.js +++ b/routes/swit.js @@ -49,6 +49,13 @@ router.get('/swits/following', authenticate, async (req, res) => { res.json(swits.reverse()); }); +router.get('/swits/user/:id', authenticate, async (req, res) => { + const { id } = req.params; + const swits = await Swit.find({ user: id }); + console.log(swits) + res.json(swits.reverse()); +}); + router.post('/swits/:id/like', authenticate, async (req, res) => { const swit = await Swit.findById(req.params.id); if (!swit) return res.status(404).send('Swit not found'); @@ -112,6 +119,58 @@ router.get('/swits/:id/comments', authenticate, async (req, res) => { res.send(swit.comments); }); + +router.post('/swits/:switId/comments/:commentId/like', authenticate, async (req, res) => { + const swit = await Swit.findById(req.params.switId); + if (!swit) return res.status(404).send('Swit not found'); + console.log(swit) + const comment = swit.comments.id(req.params.commentId); + if (!comment) return res.status(404).send('Comment not found'); + + if (comment.likes.includes(req.userId)) { + return res.status(400).json({ error: 'You have already liked this comment' }); + } + + comment.likes.push(req.userId); + await swit.save(); + + res.sendStatus(200); +}); + +router.post('/swits/:switId/comments/:commentId/unlike', authenticate, async (req, res) => { + const swit = await Swit.findById(req.params.switId); + if (!swit) return res.status(404).send('Swit not found'); + + const comment = swit.comments.id(req.params.commentId); + if (!comment) return res.status(404).send('Comment not found'); + + comment.likes = comment.likes.filter(id => id.toString() !== req.userId); + await swit.save(); + + res.sendStatus(200); +}); + +router.delete('/swits/:switId/comments/:commentId', authenticate, async (req, res) => { + const { switId, commentId } = req.params; + + const swit = await Swit.findById(switId); + if (!swit) return res.status(404).send('Swit not found'); + + const commentIndex = swit.comments.findIndex(comment => comment._id.toString() === commentId); + if (commentIndex === -1) return res.status(404).send('Comment not found'); + + // Only allow the user who posted the comment to delete it + if (swit.comments[commentIndex].user.toString() !== req.userId) { + return res.status(403).send('You do not have permission to delete this comment'); + } + + swit.comments.splice(commentIndex, 1); + await swit.save(); + + res.sendStatus(204); +}); + + router.post('/swits/:id/repost', authenticate, async (req, res) => { const swit = await Swit.findById(req.params.id); if (!swit) return res.status(404).send('Swit not found'); diff --git a/server.js b/server.js index c34b337..6f4fa67 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ const session = require('express-session'); require('dotenv').config(); const http = require('http'); const { Server } = require('socket.io'); +const dmRoutes = require('./routes/dm'); const app = express(); @@ -16,17 +17,20 @@ const server = http.createServer(app); const io = new Server(server); app.io = io; -let onlineUsers = {}; +app.onlineUsers = {}; io.on('connection', (socket) => { console.log('a user connected'); + socket.on('user connected', (userId) => { - onlineUsers[userId] = socket.id; + console.log(`User ${userId} connected`); // log when a 'user connected' message is received + app.onlineUsers[userId] = socket.id; + console.log(app.onlineUsers); // log the current state of the `onlineUsers` object }); socket.on('disconnect', () => { console.log('user disconnected'); - const userId = Object.keys(onlineUsers).find(key => onlineUsers[key] === socket.id); - if (userId) delete onlineUsers[userId]; + const userId = Object.keys(app.onlineUsers).find(key => app.onlineUsers[key] === socket.id); + if (userId) delete app.onlineUsers[userId]; }); }); @@ -55,11 +59,12 @@ app.use('/api/v1/app/auth', authRoutes); // Mount the auth routes at /auth app.use('/api/v1/app/swit', switRoutes); // Mount the swit routes at /swits app.use('/api/v1/app/user', userRoutes); // Mount the user routes at /user app.use('/api/v1/app/notify', notifyRoutes); // Mount the notify routes at /notify +app.use('/api/v1/app/dms', dmRoutes); // Mount the dm routes at /dms' app.get('/', (req, res) => { res.send('Hello, World!'); }); -app.listen(port, '0.0.0.0', () => { +server.listen(port, '0.0.0.0', () => { console.log(`Server is running on http://localhost:${port}`); }); diff --git a/uploads/1d23510b8fbf92776ecc858c09f99546 b/uploads/1d23510b8fbf92776ecc858c09f99546 new file mode 100644 index 0000000..bb640f8 Binary files /dev/null and b/uploads/1d23510b8fbf92776ecc858c09f99546 differ