feat(chat): скрытие и плановое удаление диалогов
Админ может скрыть диалог тьютора (мягкое удаление), снять скрытие, отменить плановое удаление или удалить диалог физически через TTL общей настройки корзины. - chat_threads: deleted_at, purge_at (init.sql, migration.sql, ensureChatTables) - маршруты hide / restore / unschedule / permanent под requireAdmin + аудит - purgeScheduledDeletions() + hardDeleteChatThread() с safeUnlink по вложениям - скрытые диалоги видны тьютору как обычно; новое сообщение тьютора авто-возвращает диалог в список (thread_update по SSE) - UI: кнопка «Скрытые» с бейджем и действия в карточке треда - бэкап: deleted_at/purge_at в normalizeRestoreData
This commit is contained in:
@@ -345,6 +345,42 @@ async function main() {
|
||||
const chatBadUser = await api('/api/chat/threads', { token, method: 'POST', body: { user_id: 99999999 } });
|
||||
ok('chat: несуществующий тьютор -> 404', chatBadUser.status === 404, chatBadUser.status);
|
||||
|
||||
// --- Chat: скрытие/восстановление диалога (только админ) ---
|
||||
const chatHideThread = await api('/api/chat/threads', { token, method: 'POST', body: { user_id: me1.data.id } });
|
||||
if (chatHideThread.status === 200 && chatHideThread.data.thread) {
|
||||
const hid = chatHideThread.data.thread.id;
|
||||
const hideOk = await api(`/api/chat/threads/${hid}`, { token, method: 'DELETE' });
|
||||
ok('chat: админ скрывает диалог', hideOk.status === 200, hideOk.status);
|
||||
const hideAgain = await api(`/api/chat/threads/${hid}`, { token, method: 'DELETE' });
|
||||
ok('chat: повторное скрытие -> 400', hideAgain.status === 400, hideAgain.status);
|
||||
const listActive = await api('/api/chat/threads', { token });
|
||||
ok('chat: скрытый диалог ушёл из активного списка',
|
||||
listActive.status === 200 && !(listActive.data.items || []).some(t => t.id === hid)
|
||||
&& listActive.data.hidden >= 1 && listActive.data.hidden_mode === false,
|
||||
{ hidden: listActive.data.hidden, mode: listActive.data.hidden_mode });
|
||||
const listHidden = await api('/api/chat/threads?deleted=1', { token });
|
||||
ok('chat: скрытый диалог виден в скрытых',
|
||||
listHidden.status === 200 && (listHidden.data.items || []).some(t => t.id === hid)
|
||||
&& listHidden.data.hidden_mode === true && !!(listHidden.data.items || []).find(t => t.id === hid).deleted_at,
|
||||
(listHidden.data.items || []).map(t => t.id));
|
||||
const messagesHidden = await api(`/api/chat/threads/${hid}/messages`, { token });
|
||||
ok('chat: сообщения скрытого диалога читаются', messagesHidden.status === 200 && Array.isArray(messagesHidden.data.items), messagesHidden.status);
|
||||
const restore = await api(`/api/chat/threads/${hid}/restore`, { token, method: 'PUT' });
|
||||
ok('chat: восстановление диалога', restore.status === 200, restore.status);
|
||||
const restoreAgain = await api(`/api/chat/threads/${hid}/restore`, { token, method: 'PUT' });
|
||||
ok('chat: повторное восстановление -> 400', restoreAgain.status === 400, restoreAgain.status);
|
||||
const hideSchedule = await api(`/api/chat/threads/${hid}`, { token, method: 'DELETE' });
|
||||
ok('chat: скрытие перед планированием удаления', hideSchedule.status === 200, hideSchedule.status);
|
||||
const purge = await api(`/api/chat/threads/${hid}/permanent`, { token, method: 'DELETE' });
|
||||
ok('chat: планирование удаления диалога', purge.status === 200, purge.status);
|
||||
const purgeAgain = await api(`/api/chat/threads/${hid}/permanent`, { token, method: 'DELETE' });
|
||||
ok('chat: повторное планирование продлевает срок', purgeAgain.status === 200, purgeAgain.status);
|
||||
const restoreScheduled = await api(`/api/chat/threads/${hid}/restore`, { token, method: 'PUT' });
|
||||
ok('chat: восстановление снимает планирование', restoreScheduled.status === 200, restoreScheduled.status);
|
||||
}
|
||||
const chatHideNoAuth = await api(`/api/chat/threads/${chatHideThread.data?.thread?.id || 1}`, { method: 'DELETE' });
|
||||
ok('chat: скрытие без токена -> 401', chatHideNoAuth.status === 401, chatHideNoAuth.status);
|
||||
|
||||
// --- Chat: правка и удаление сообщений ---
|
||||
const tutorLogin = 'smoke_chat_' + Date.now().toString(36);
|
||||
const tutorPass = 'SmokeChat123!';
|
||||
|
||||
Reference in New Issue
Block a user