deletion reason and email for soft deleted forum posts and comments by admin

This commit is contained in:
jackiettran
2025-11-20 18:08:30 -05:00
parent b2f18d77f6
commit f7767dfd13
10 changed files with 911 additions and 21 deletions

View File

@@ -1250,26 +1250,68 @@ router.get('/tags', async (req, res) => {
// DELETE /api/forum/admin/posts/:id - Admin soft-delete post
router.delete('/admin/posts/:id', authenticateToken, requireAdmin, async (req, res) => {
try {
const post = await ForumPost.findByPk(req.params.id);
const { reason } = req.body;
if (!reason || !reason.trim()) {
return res.status(400).json({ error: "Deletion reason is required" });
}
const post = await ForumPost.findByPk(req.params.id, {
include: [
{
model: User,
as: 'author',
attributes: ['id', 'username', 'firstName', 'lastName', 'email']
}
]
});
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
if (post.isDeleted) {
return res.status(400).json({ error: 'Post is already deleted' });
}
// Soft delete the post
await post.update({
isDeleted: true,
deletedBy: req.user.id,
deletedAt: new Date()
deletedAt: new Date(),
deletionReason: reason.trim()
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Admin deleted post", {
postId: req.params.id,
adminId: req.user.id,
originalAuthorId: post.authorId
originalAuthorId: post.authorId,
reason: reason.trim()
});
// Send email notification to post author (non-blocking)
(async () => {
try {
const admin = await User.findByPk(req.user.id, {
attributes: ['id', 'username', 'firstName', 'lastName']
});
if (post.author && admin) {
await emailServices.forum.sendForumPostDeletionNotification(
post.author,
admin,
post,
reason.trim()
);
console.log(`Forum post deletion notification email sent to author ${post.authorId}`);
}
} catch (emailError) {
// Log but don't fail the deletion
console.error('Failed to send forum post deletion notification email:', emailError.message);
}
})();
res.status(200).json({ message: 'Post deleted successfully', post });
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
@@ -1292,11 +1334,16 @@ router.patch('/admin/posts/:id/restore', authenticateToken, requireAdmin, async
return res.status(404).json({ error: 'Post not found' });
}
if (!post.isDeleted) {
return res.status(400).json({ error: 'Post is not deleted' });
}
// Restore the post
await post.update({
isDeleted: false,
deletedBy: null,
deletedAt: null
deletedAt: null,
deletionReason: null
});
const reqLogger = logger.withRequestId(req.id);
@@ -1322,25 +1369,44 @@ router.patch('/admin/posts/:id/restore', authenticateToken, requireAdmin, async
// DELETE /api/forum/admin/comments/:id - Admin soft-delete comment
router.delete('/admin/comments/:id', authenticateToken, requireAdmin, async (req, res) => {
try {
const comment = await ForumComment.findByPk(req.params.id);
const { reason } = req.body;
if (!reason || !reason.trim()) {
return res.status(400).json({ error: "Deletion reason is required" });
}
const comment = await ForumComment.findByPk(req.params.id, {
include: [
{
model: User,
as: 'author',
attributes: ['id', 'username', 'firstName', 'lastName', 'email']
}
]
});
if (!comment) {
return res.status(404).json({ error: 'Comment not found' });
}
if (comment.isDeleted) {
return res.status(400).json({ error: 'Comment is already deleted' });
}
// Soft delete the comment (preserve original content for potential restoration)
await comment.update({
isDeleted: true,
deletedBy: req.user.id,
deletedAt: new Date()
deletedAt: new Date(),
deletionReason: reason.trim()
});
// Decrement comment count if not already deleted
if (!comment.isDeleted) {
const post = await ForumPost.findByPk(comment.postId);
if (post && post.commentCount > 0) {
await post.decrement('commentCount');
}
// Decrement comment count
const post = await ForumPost.findByPk(comment.postId, {
attributes: ['id', 'title']
});
if (post && post.commentCount > 0) {
await post.decrement('commentCount');
}
const reqLogger = logger.withRequestId(req.id);
@@ -1348,9 +1414,32 @@ router.delete('/admin/comments/:id', authenticateToken, requireAdmin, async (req
commentId: req.params.id,
adminId: req.user.id,
originalAuthorId: comment.authorId,
postId: comment.postId
postId: comment.postId,
reason: reason.trim()
});
// Send email notification to comment author (non-blocking)
(async () => {
try {
const admin = await User.findByPk(req.user.id, {
attributes: ['id', 'username', 'firstName', 'lastName']
});
if (comment.author && admin && post) {
await emailServices.forum.sendForumCommentDeletionNotification(
comment.author,
admin,
post,
reason.trim()
);
console.log(`Forum comment deletion notification email sent to author ${comment.authorId}`);
}
} catch (emailError) {
// Log but don't fail the deletion
console.error('Failed to send forum comment deletion notification email:', emailError.message);
}
})();
res.status(200).json({ message: 'Comment deleted successfully', comment });
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
@@ -1381,7 +1470,8 @@ router.patch('/admin/comments/:id/restore', authenticateToken, requireAdmin, asy
await comment.update({
isDeleted: false,
deletedBy: null,
deletedAt: null
deletedAt: null,
deletionReason: null
});
// Increment comment count