fix(shorts): плановая очистка, бэкап, часовой пояс и экранирование

- purgeScheduledDeletions() теперь чистит short_messages по purge_at
- short_messages добавлена в BACKUP_TABLES/BACKUP_SEQUENCE_TABLES,
  normalizeRestoreData и restore (поле автора проверяется на живость)
- сид short_messages_retention_days перенесён и в db/init.sql
- used_at по умолчанию берётся в зоне приложения (appTodayIso),
  а не по UTC
- поле «Использовано» скрыто при создании, открыто при правке
- удалён пустой escInline и мёртвые data-message/canEdit: сырой
  текст сообщения попадал в атрибут неэкранированным
This commit is contained in:
dev
2026-10-06 00:06:29 +03:00
parent ee09958644
commit d00de2cb1a
6 changed files with 73 additions and 19 deletions
+20
View File
@@ -79,6 +79,26 @@ const ch = normalizeRestoreData({
ok('chat_threads нормализуются', ch.chat_threads[0].tutor_id === 1 && ch.chat_threads[0].tutor_unread === 2, ch.chat_threads[0]);
ok('chat_messages нормализуются', ch.chat_messages[0].thread_id === 3 && ch.chat_messages[0].body === 'привет', ch.chat_messages[0]);
ok('chat_attachments нормализуются', ch.chat_attachments[0].name === 'photo.png' && ch.chat_attachments[0].size === 1234, ch.chat_attachments[0]);
const sm = normalizeRestoreData({
...base,
short_messages: [{ id: 1, resident: 'Иванов И.И.', lesson_date: '2026-05-05', topic: 'T', message: 'текст', used: true, used_at: '2026-05-06', author_id: 1, deleted_at: '2026-05-07T00:00:00.000Z', purge_at: '2027-05-07T00:00:00.000Z' }],
});
ok('short_messages нормализуются', sm.short_messages[0].resident === 'Иванов И.И.' && sm.short_messages[0].used === true && sm.short_messages[0].used_at === '2026-05-06', sm.short_messages[0]);
ok('short_messages сохраняет purge_at', sm.short_messages[0].deleted_at === '2026-05-07T00:00:00.000Z' && sm.short_messages[0].purge_at === '2027-05-07T00:00:00.000Z', sm.short_messages[0]);
let smBadDate = false;
try {
normalizeRestoreData({ ...base, short_messages: [{ id: 1, resident: 'X', lesson_date: '05.05.2026', message: 't' }] });
} catch (e) {
smBadDate = true;
}
ok('кривая дата занятия в short_messages отклоняется', smBadDate);
let smNoResident = false;
try {
normalizeRestoreData({ ...base, short_messages: [{ id: 1, resident: '', lesson_date: '2026-05-05', message: 't' }] });
} catch (e) {
smNoResident = true;
}
ok('пустой резидент в short_messages отклоняется', smNoResident);
const chEdits = normalizeRestoreData({
...base,
chat_messages: [