Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 20 additions & 96 deletions server/controllers/answersController.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,38 @@
const Answer = require('../models/Answer');
const Notification = require('../models/Notification');

// Helper function to create notifications
const createNotification = async (recipientId, senderId, type, content, questionId, answerId) => {
try {
// Don't create notification if user is voting on their own content
if (recipientId.toString() === senderId.toString()) {
return;
}

await Notification.create({
recipient: recipientId,
sender: senderId,
type: type,
questionId: questionId,
answerId: answerId
}).then(notification => {
console.log('📩 Notification created:', notification);
});
} catch (error) {
console.error('❌ Error creating notification:', error);
}
};
const { voteOnContent, sendVoteResponse } = require('../services/voting');

// Upvote Answer
exports.upvoteAnswer = async (req, res) => {
try {
console.log('⬆️ Upvote called:', req.params.answerId, 'User:', req.user._id);
const answer = await Answer.findById(req.params.answerId).populate('author', 'username');
if (!answer) return res.status(404).json({ message: 'Answer not found' });
const result = await voteOnContent({
model: Answer,
contentId: req.params.answerId,
voter: req.user,
voteType: 'upvote',
contentType: 'answer'
});

const userId = req.user._id.toString();

// Check if user already upvoted
const existingUpvote = answer.votes.upvotes.find(vote => vote.user.toString() === userId);
const existingDownvote = answer.votes.downvotes.find(vote => vote.user.toString() === userId);

if (existingUpvote) {
// Remove upvote (toggle off)
answer.votes.upvotes = answer.votes.upvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬆️ Upvote removed');
} else {
// Add upvote and remove downvote if exists
answer.votes.upvotes.push({ user: req.user._id });
answer.votes.downvotes = answer.votes.downvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬆️ Upvote added');

// Create notification for answer author
await createNotification(
answer.author._id,
req.user._id,
'upvote',
`${req.user.username} upvoted your answer`,
answer.question,
answer._id
);
}

await answer.save();
await answer.populate('author', 'username avatar reputation');

console.log('✅ Upvote successful. New vote count:', answer.voteCount);
res.status(200).json(answer);
sendVoteResponse(res, { ...result, contentType: 'answer' });
} catch (error) {
console.error('❌ Upvote error:', error);
res.status(500).json({ message: 'Server error', error: error.message });
res.status(error.statusCode || 500).json({ message: error.statusCode ? error.message : 'Server error', error: error.message });
}
};

// Downvote Answer
exports.downvoteAnswer = async (req, res) => {
try {
console.log('⬇️ Downvote called:', req.params.answerId, 'User:', req.user._id);
const answer = await Answer.findById(req.params.answerId).populate('author', 'username');
if (!answer) return res.status(404).json({ message: 'Answer not found' });
const result = await voteOnContent({
model: Answer,
contentId: req.params.answerId,
voter: req.user,
voteType: 'downvote',
contentType: 'answer'
});

const userId = req.user._id.toString();

// Check if user already downvoted
const existingDownvote = answer.votes.downvotes.find(vote => vote.user.toString() === userId);
const existingUpvote = answer.votes.upvotes.find(vote => vote.user.toString() === userId);

if (existingDownvote) {
// Remove downvote (toggle off)
answer.votes.downvotes = answer.votes.downvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬇️ Downvote removed');
} else {
// Add downvote and remove upvote if exists
answer.votes.downvotes.push({ user: req.user._id });
answer.votes.upvotes = answer.votes.upvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬇️ Downvote added');

// Create notification for answer author
await createNotification(
answer.author._id,
req.user._id,
'downvote',
`${req.user.username} downvoted your answer`,
answer.question,
answer._id
);
}

await answer.save();
await answer.populate('author', 'username avatar reputation');

console.log('✅ Downvote successful. New vote count:', answer.voteCount);
res.status(200).json(answer);
sendVoteResponse(res, { ...result, contentType: 'answer' });
} catch (error) {
console.error('❌ Downvote error:', error);
res.status(500).json({ message: 'Server error', error: error.message });
res.status(error.statusCode || 500).json({ message: error.statusCode ? error.message : 'Server error', error: error.message });
}
};
};
137 changes: 29 additions & 108 deletions server/routes/questions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const express = require('express')
const { body, validationResult } = require('express-validator')
const Question = require('../models/Question')
const Answer = require('../models/Answer')
const User = require('../models/User')
const { authenticateToken, requireAdmin } = require('../middleware/auth')
const { authenticateToken } = require('../middleware/auth')
const { acceptAnswer } = require('../services/answerAcceptance')
const { voteOnContent, sendVoteResponse } = require('../services/voting')

const router = express.Router()

Expand Down Expand Up @@ -377,51 +377,18 @@ router.delete('/:id', authenticateToken, async (req, res) => {
// @access Private
router.post('/:id/upvote', authenticateToken, async (req, res) => {
try {
console.log('⬆️ Question upvote called:', req.params.id, 'User:', req.user._id);
const question = await Question.findById(req.params.id).populate('author', 'username');

if (!question || question.isDeleted) {
return res.status(404).json({ message: 'Question not found' });
}
const result = await voteOnContent({
model: Question,
contentId: req.params.id,
voter: req.user,
voteType: 'upvote',
contentType: 'question'
})

const userId = req.user._id.toString();
const existingUpvote = question.votes.upvotes.find(vote => vote.user.toString() === userId);
const existingDownvote = question.votes.downvotes.find(vote => vote.user.toString() === userId);

if (existingUpvote) {
// Remove upvote (toggle off)
question.votes.upvotes = question.votes.upvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬆️ Question upvote removed');
} else {
// Add upvote and remove downvote if exists
question.votes.upvotes.push({ user: req.user._id });
question.votes.downvotes = question.votes.downvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬆️ Question upvote added');

// Create notification for question author (if not self)
if (question.author._id.toString() !== req.user._id.toString()) {
try {
const notification = await require('../models/Notification').create({
recipient: question.author._id,
sender: req.user._id,
type: 'upvote',
questionId: question._id
});
console.log('📩 Notification created:', notification);
} catch (error) {
console.error('❌ Error creating question upvote notification:', error);
}
}
}

await question.save();
await question.populate('author', 'username reputation avatar');

console.log('✅ Question upvote successful. New vote count:', question.voteCount);
res.status(200).json(question);
sendVoteResponse(res, { ...result, contentType: 'question' })
} catch (error) {
console.error('❌ Question upvote error:', error);
res.status(500).json({ message: 'Server error', error: error.message });
res.status(error.statusCode || 500).json({ message: error.statusCode ? error.message : 'Server error', error: error.message });
}
});

Expand All @@ -430,51 +397,18 @@ router.post('/:id/upvote', authenticateToken, async (req, res) => {
// @access Private
router.post('/:id/downvote', authenticateToken, async (req, res) => {
try {
console.log('⬇️ Question downvote called:', req.params.id, 'User:', req.user._id);
const question = await Question.findById(req.params.id).populate('author', 'username');

if (!question || question.isDeleted) {
return res.status(404).json({ message: 'Question not found' });
}
const result = await voteOnContent({
model: Question,
contentId: req.params.id,
voter: req.user,
voteType: 'downvote',
contentType: 'question'
})

const userId = req.user._id.toString();
const existingDownvote = question.votes.downvotes.find(vote => vote.user.toString() === userId);
const existingUpvote = question.votes.upvotes.find(vote => vote.user.toString() === userId);

if (existingDownvote) {
// Remove downvote (toggle off)
question.votes.downvotes = question.votes.downvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬇️ Question downvote removed');
} else {
// Add downvote and remove upvote if exists
question.votes.downvotes.push({ user: req.user._id });
question.votes.upvotes = question.votes.upvotes.filter(vote => vote.user.toString() !== userId);
console.log('⬇️ Question downvote added');

// Create notification for question author (if not self)
if (question.author._id.toString() !== req.user._id.toString()) {
try {
const notification = await require('../models/Notification').create({
recipient: question.author._id,
sender: req.user._id,
type: 'downvote',
questionId: question._id
});
console.log('📩 Notification created:', notification);
} catch (error) {
console.error('❌ Error creating question downvote notification:', error);
}
}
}

await question.save();
await question.populate('author', 'username reputation avatar');

console.log('✅ Question downvote successful. New vote count:', question.voteCount);
res.status(200).json(question);
sendVoteResponse(res, { ...result, contentType: 'question' })
} catch (error) {
console.error('❌ Question downvote error:', error);
res.status(500).json({ message: 'Server error', error: error.message });
res.status(error.statusCode || 500).json({ message: error.statusCode ? error.message : 'Server error', error: error.message });
}
});

Expand All @@ -496,33 +430,20 @@ router.post('/:id/vote', [
})
}

const question = await Question.findById(req.params.id)

if (!question || question.isDeleted) {
return res.status(404).json({ message: 'Question not found' })
}

// Check if user can vote
if (!req.user.canVote()) {
return res.status(403).json({ message: 'Insufficient reputation to vote' })
}

const { voteType } = req.body

await question.addVote(req.user._id, voteType)

// Update author reputation
const reputationChange = voteType === 'upvote' ? 10 : -2
await question.author.updateReputation(reputationChange)

res.json({
message: 'Vote recorded successfully',
voteCount: question.voteCount,
totalVotes: question.totalVotes
const result = await voteOnContent({
model: Question,
contentId: req.params.id,
voter: req.user,
voteType,
contentType: 'question'
})

sendVoteResponse(res, { ...result, contentType: 'question' })
} catch (error) {
console.error('Vote error:', error)
res.status(500).json({ message: 'Server error' })
res.status(error.statusCode || 500).json({ message: error.statusCode ? error.message : 'Server error' })
}
})

Expand Down
Loading
Loading