Spaces:
Sleeping
Sleeping
File size: 4,150 Bytes
f6c14df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | const { query } = require('../utils/db');
const { auditLog } = require('../utils/auditLogger');
const { v4: uuidv4 } = require('uuid');
/**
* GET /api/tasks/:id/comments
*/
const listComments = async (req, res) => {
try {
const { organization_id: orgId, id: userId, role } = req.user;
const { id: taskId } = req.params;
// Verify task belongs to org
const taskCheck = await query(
`SELECT id, creator_id, assignee_id FROM tasks WHERE id = $1 AND organization_id = $2`,
[taskId, orgId]
);
if (!taskCheck.rows.length) return res.status(404).json({ error: 'Task not found.' });
const task = taskCheck.rows[0];
if (role === 'member' && task.creator_id !== userId && task.assignee_id !== userId) {
return res.status(403).json({ error: 'Access denied.' });
}
const { rows } = await query(
`SELECT c.id, c.body, c.created_at, c.updated_at,
c.author_id, u.name AS author_name, u.email AS author_email
FROM task_comments c
JOIN users u ON u.id = c.author_id
WHERE c.task_id = $1 AND c.organization_id = $2
ORDER BY c.created_at ASC`,
[taskId, orgId]
);
return res.json(rows);
} catch (err) {
console.error('List comments error:', err);
return res.status(500).json({ error: 'Failed to retrieve comments.' });
}
};
/**
* POST /api/tasks/:id/comments
*/
const addComment = async (req, res) => {
try {
const { organization_id: orgId, id: userId, name: userName, email: userEmail, role } = req.user;
const { id: taskId } = req.params;
const { body } = req.body;
if (!body || !body.trim()) {
return res.status(422).json({ error: 'Comment cannot be empty.' });
}
const taskCheck = await query(
`SELECT id, creator_id, assignee_id FROM tasks WHERE id = $1 AND organization_id = $2`,
[taskId, orgId]
);
if (!taskCheck.rows.length) return res.status(404).json({ error: 'Task not found.' });
const task = taskCheck.rows[0];
if (role === 'member' && task.creator_id !== userId && task.assignee_id !== userId) {
return res.status(403).json({ error: 'Access denied.' });
}
const { rows } = await query(
`INSERT INTO task_comments (id, task_id, organization_id, author_id, body)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, body, created_at, author_id`,
[uuidv4(), taskId, orgId, userId, body.trim()]
);
// Touch task updated_at so it surfaces in recent activity
await query(`UPDATE tasks SET updated_at = NOW() WHERE id = $1`, [taskId]);
await auditLog({
organizationId: orgId,
taskId,
actorId: userId,
actorName: userName,
actorEmail: userEmail,
action: 'COMMENT_ADDED',
entityType: 'comment',
newValues: { body: body.trim().slice(0, 120) },
});
return res.status(201).json({
...rows[0],
author_name: userName,
author_email: userEmail,
});
} catch (err) {
console.error('Add comment error:', err);
return res.status(500).json({ error: 'Failed to add comment.' });
}
};
/**
* DELETE /api/tasks/:id/comments/:commentId
*/
const deleteComment = async (req, res) => {
try {
const { organization_id: orgId, id: userId, role } = req.user;
const { id: taskId, commentId } = req.params;
const { rows } = await query(
`SELECT c.id, c.author_id FROM task_comments c
JOIN tasks t ON t.id = c.task_id
WHERE c.id = $1 AND c.task_id = $2 AND c.organization_id = $3`,
[commentId, taskId, orgId]
);
if (!rows.length) return res.status(404).json({ error: 'Comment not found.' });
const comment = rows[0];
if (role !== 'admin' && comment.author_id !== userId) {
return res.status(403).json({ error: 'You can only delete your own comments.' });
}
await query(`DELETE FROM task_comments WHERE id = $1`, [commentId]);
return res.status(204).send();
} catch (err) {
console.error('Delete comment error:', err);
return res.status(500).json({ error: 'Failed to delete comment.' });
}
};
module.exports = { listComments, addComment, deleteComment };
|