feat(chat): правка и удаление сообщений

- PUT/DELETE /api/chat/threads/:id/messages/:messageId под requireAuth+chatLimiter,
  доступ через chatMessageFor (тред + принадлежность сообщения)
- править может только автор (админу чужое сообщение 403), удалять — автор или админ
- мягкое удаление: body='', вложения каскадом из БД + safeUnlink по path,
  счётчик непрочитанных уменьшается на 1 у своей стороны (GREATEST(col-1,0))
- refreshChatThreadPreview() пересчитывает превью треда по последнему живому сообщению
- SSE: publishChat с type, список типов валидируется через CHAT_EVENTS; новые
  message_update/message_delete подписаны в connectChatStream (admin.js)
- аудит chat.message.update с textDiff и chat.message.delete без текста
- колонки edited_at/deleted_at в db/init.sql, db/migration.sql, ensureChatTables()
  и в normalizeRestoreData + restore-INSERT — удалённые сообщения не воскресают
- фронтенд: кнопки правки/удаления по наведению, модалка правки (Ctrl+Enter),
  метка «изменено», плейсхолдер «Сообщение удалено», обновление по SSE
- покрытие в api.smoketest.js (правка/удаление/403/400/404) и backup.selftest.js
This commit is contained in:
dev
2026-10-05 18:32:58 +03:00
parent 7e4b1a1324
commit 1740c341a0
11 changed files with 478 additions and 12 deletions
+12
View File
@@ -76,6 +76,18 @@
</div>
</div>
<div class="modal-overlay" id="chatEditModal">
<div class="edit-modal chat-edit-modal">
<h3>Редактировать сообщение</h3>
<textarea id="chatEditText" class="settings-input" maxlength="5000"></textarea>
<div class="hint">Ctrl+Enter — сохранить. Вложения сообщения останутся как есть.</div>
<div class="card-foot" style="justify-content:flex-end;gap:8px">
<button type="button" class="btn-primary ghost" id="chatEditCancel">Отмена</button>
<button type="submit" class="btn-primary" id="chatEditSave">Сохранить</button>
</div>
</div>
</div>
<div class="modal-overlay" id="imgModal">
<img id="imgModalSrc">
</div>