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:
+11
-1
@@ -62,6 +62,14 @@ function optStr(v, max) {
|
||||
return reqStr(v, max);
|
||||
}
|
||||
|
||||
function optText(v, max) {
|
||||
if (v === null || v === undefined) return '';
|
||||
if (typeof v !== 'string') throw new Error('Invalid string');
|
||||
const s = v.trim();
|
||||
if (s.length > max) throw new Error('Invalid string length');
|
||||
return s;
|
||||
}
|
||||
|
||||
function optTs(v) {
|
||||
if (v === null || v === undefined) return null;
|
||||
if (typeof v !== 'string' || !/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(v)) throw new Error('Invalid timestamp');
|
||||
@@ -476,7 +484,9 @@ function normalizeRestoreData(data) {
|
||||
id: reqInt(x.id),
|
||||
thread_id: reqInt(x.thread_id),
|
||||
sender_id: reqInt(x.sender_id),
|
||||
body: optStr(x.body, 5000) ?? '',
|
||||
body: optText(x.body, 5000),
|
||||
edited_at: optTs(x.edited_at),
|
||||
deleted_at: optTs(x.deleted_at),
|
||||
created_at: optTs(x.created_at),
|
||||
}));
|
||||
const chat_attachments = (data.chat_attachments || []).map(x => ({
|
||||
|
||||
Reference in New Issue
Block a user