Spaces:
Sleeping
Sleeping
| 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 }; | |