feat: version 2.12.0 -- editar y borrar comentarios
Hasta ahora un comentario (de proyecto o actividad) solo se podia crear, nunca tocar. Se agrega PUT/DELETE /comments/:id, restringido a quien lo escribio o a superadmin/admin (igual criterio que useria para notas/tareas: nunca otro colaborador aunque tenga permiso de escritura en el proyecto). Migracion 021 agrega updated_at a comments para mostrar "Editado" igual que ya hace Notas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,3 +33,29 @@ export const postComment = asyncHandler(async (req, res) => {
|
||||
await logAudit({ userId: req.currentUser.id, action: 'comment', entityType, entityId, detail: { commentId: comment.id } });
|
||||
ok(res, comment, 201);
|
||||
});
|
||||
|
||||
// Solo el autor, o superadmin/admin, puede editar/borrar -- nunca otro colaborador aunque tenga
|
||||
// permiso de escritura en el proyecto (un comentario es responsabilidad de quien lo escribio).
|
||||
function canManageComment(currentUser, comment) {
|
||||
return comment.user_id === currentUser.id || [ROLES.SUPERADMIN, ROLES.ADMIN].includes(currentUser.roleCode);
|
||||
}
|
||||
|
||||
export const putComment = asyncHandler(async (req, res) => {
|
||||
const comment = await repo.findById(Number(req.params.id));
|
||||
if (!comment) throw new HttpError(404, 'Comentario no encontrado');
|
||||
if (!canManageComment(req.currentUser, comment)) throw new HttpError(403, 'Solo quien escribio el comentario puede editarlo');
|
||||
const message = (req.body?.message || '').trim();
|
||||
if (!message) throw new HttpError(400, 'El mensaje es obligatorio');
|
||||
const updated = await repo.update(comment.id, message);
|
||||
await logAudit({ userId: req.currentUser.id, action: 'comment_edit', entityType: comment.entity_type, entityId: comment.entity_id, detail: { commentId: comment.id } });
|
||||
ok(res, updated);
|
||||
});
|
||||
|
||||
export const deleteComment = asyncHandler(async (req, res) => {
|
||||
const comment = await repo.findById(Number(req.params.id));
|
||||
if (!comment) throw new HttpError(404, 'Comentario no encontrado');
|
||||
if (!canManageComment(req.currentUser, comment)) throw new HttpError(403, 'Solo quien escribio el comentario puede borrarlo');
|
||||
await repo.remove(comment.id);
|
||||
await logAudit({ userId: req.currentUser.id, action: 'comment_delete', entityType: comment.entity_type, entityId: comment.entity_id, detail: { commentId: comment.id } });
|
||||
ok(res, { deleted: true });
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { pool } from '../../config/db.js';
|
||||
|
||||
export async function findByEntity(entityType, entityId) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT c.id, c.entity_type, c.entity_id, c.user_id, u.name AS user_name, c.message, c.created_at
|
||||
`SELECT c.id, c.entity_type, c.entity_id, c.user_id, u.name AS user_name, c.message, c.created_at, c.updated_at
|
||||
FROM comments c JOIN users u ON u.id = c.user_id
|
||||
WHERE c.entity_type = ? AND c.entity_id = ? ORDER BY c.created_at`,
|
||||
[entityType, entityId]
|
||||
@@ -10,6 +10,24 @@ export async function findByEntity(entityType, entityId) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function findById(id) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT c.id, c.entity_type, c.entity_id, c.user_id, u.name AS user_name, c.message, c.created_at, c.updated_at
|
||||
FROM comments c JOIN users u ON u.id = c.user_id WHERE c.id = ?`,
|
||||
[id]
|
||||
);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
export async function update(id, message) {
|
||||
await pool.query('UPDATE comments SET message = ? WHERE id = ?', [message, id]);
|
||||
return findById(id);
|
||||
}
|
||||
|
||||
export async function remove(id) {
|
||||
await pool.query('DELETE FROM comments WHERE id = ?', [id]);
|
||||
}
|
||||
|
||||
// Usado al borrar la entidad duena (actividad, proyecto): limpia sus comentarios de paso, ya que
|
||||
// no hay FK fisica cruzada (entity_type/entity_id es polimorfico) que lo haga por cascada.
|
||||
export async function removeByEntity(entityType, entityId) {
|
||||
@@ -27,10 +45,5 @@ export async function create({ entityType, entityId, userId, message }) {
|
||||
'INSERT INTO comments (entity_type, entity_id, user_id, message) VALUES (?, ?, ?, ?)',
|
||||
[entityType, entityId, userId, message]
|
||||
);
|
||||
const [rows] = await pool.query(
|
||||
`SELECT c.id, c.entity_type, c.entity_id, c.user_id, u.name AS user_name, c.message, c.created_at
|
||||
FROM comments c JOIN users u ON u.id = c.user_id WHERE c.id = ?`,
|
||||
[result.insertId]
|
||||
);
|
||||
return rows[0];
|
||||
return findById(result.insertId);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import { requireAuth } from '../../middleware/auth.js';
|
||||
import { getComments, postComment } from './controller.js';
|
||||
import { getComments, postComment, putComment, deleteComment } from './controller.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use(requireAuth);
|
||||
router.get('/', getComments);
|
||||
router.post('/', postComment);
|
||||
router.put('/:id', putComment);
|
||||
router.delete('/:id', deleteComment);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user