refactor: move frontend JS to external files and enable strict CSP

This commit is contained in:
dev
2026-09-09 10:06:49 +03:00
parent d6e589d2f5
commit 018c65adc5
34 changed files with 2444 additions and 2307 deletions
+2 -73
View File
@@ -17,7 +17,7 @@
<div class="actions-row">
<input type="search" id="auditFilter" class="settings-input" placeholder="Фильтр по действию..." style="max-width:300px">
<button class="btn-link" onclick="loadAudit()" title="Обновить">Обновить</button>
<button class="btn-link" id="auditRefreshBtn" type="button" title="Обновить">Обновить</button>
<span class="hint" id="auditCount"></span>
</div>
@@ -39,77 +39,6 @@
</div>
<div class="toast" id="toast"></div>
<script src="admin.js"></script>
<script>
const ACTION_LABELS = {
'settings.update': 'Изменены настройки',
'group.create': 'Создана группа',
'group.update': 'Изменена группа',
'group.delete': 'Удалена группа',
'group.photo.create': 'Загружена фото группы',
'group.photo.update': 'Изменено фото группы',
'group.photo.delete': 'Удалено фото группы',
'student.create': 'Добавлен ученик',
'student.update': 'Изменён ученик',
'student.delete': 'Удалён ученик',
'student.batch-group': 'Ученики перенесены в группу',
'entry.update': 'Изменена запись',
'entry.soft-delete': 'Запись в корзину',
'entry.restore': 'Запись восстановлена',
'entry.permanent-delete': 'Запись удалена безвозвратно',
'link.create': 'Создана ссылка',
'link.update': 'Изменена ссылка',
'link.delete': 'Удалена ссылка',
'backup.download': 'Скачан бэкап',
'backup.restore': 'Выполнено восстановление',
};
function fmtTime(t) {
try { return new Date(t).toLocaleString('ru'); } catch { return t || ''; }
}
function dt(t) {
if (!t) return '';
if (typeof t === 'object') {
return esc(JSON.stringify(t).slice(0, 120));
}
try {
const o = JSON.parse(t);
return esc(JSON.stringify(o).slice(0, 120));
} catch { return esc(String(t).slice(0, 120)); }
}
async function loadAudit() {
const el = document.getElementById('auditTable').querySelector('tbody');
el.innerHTML = '<tr><td colspan="4" style="color:var(--muted)">Загрузка…</td></tr>';
const res = await fetch(`${API}/api/audit?limit=500`, { headers: hdr() });
if (!res.ok) { el.innerHTML = '<tr><td colspan="4" style="color:var(--muted)">Ошибка загрузки</td></tr>'; return; }
const rows = await res.json();
document.getElementById('auditCount').textContent = rows.length ? `Записей: ${rows.length}` : '';
const q = (document.getElementById('auditFilter').value || '').toLowerCase().trim();
const filtered = q ? rows.filter(r => (ACTION_LABELS[r.action] || r.action).toLowerCase().includes(q)) : rows;
if (!filtered.length) {
document.getElementById('auditEmpty').style.display = 'block';
el.innerHTML = '';
return;
}
document.getElementById('auditEmpty').style.display = 'none';
el.innerHTML = filtered.map(r => `
<tr>
<td style="white-space:nowrap">${fmtTime(r.created_at)}</td>
<td><b>${esc(ACTION_LABELS[r.action] || r.action)}</b></td>
<td style="color:var(--muted)">${dt(r.target)}</td>
<td style="color:var(--muted);white-space:nowrap">${esc(r.ip || '')}</td>
</tr>`).join('');
}
document.getElementById('auditFilter').addEventListener('input', loadAudit);
(async () => {
if (await checkAuth()) {
buildSidebar(document.body.dataset.page);
loadAudit();
}
})();
</script>
<script src="js/audit.js"></script>
</body>
</html>