feat(panel): обновление из репо, обслуживание БД, авто-прунинг, цветные логи и Сторож

- [U] Обновление из репозитория: бэкап -> git pull --ff-only -> npm install -> миграции
  -> рестарт -> health-check; при провале миграций/health предлагает откат (git reset --hard
  + восстановление БД из свежего бэкапа). Текущая версия (git short-hash + subject) в шапке.
- [M] Обслуживание БД: backend/scripts/db-maintain.js (node:sqlite) — integrity_check ->
  WAL checkpoint(TRUNCATE) -> VACUUM; VACUUM пропускается на битой БД. Авто-бэкап + стоп/старт.
- Авто-прунинг бэкапов: Backup-Db хранит последних 10 (Prune-Backups), Copy-DbFrom вынесен
  общим (реюз в Restore-Db и откате обновления), запоминается путь последнего бэкапа.
- Живые логи: отдельный tools/tail-logs.ps1 — раскраска уровней (ERROR/FATAL красным,
  WARN жёлтым, успех зелёным) вместо сырого tail; вынос из inline-команды (PS 5.1 quoting).
- Экран «Сторож»: дашборд в рамке с перерисовкой — статус-маркер, счётчики проверок/
  перезапусков, последнее событие; выход по клавише.
Все .ps1 — UTF-8 BOM, парсинг OK; db-maintain протестирован на копии БД (10.7->10.5 МБ);
рендер-смоук подтвердил выравнивание.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-19 23:10:52 +03:00
parent 27f51f1a61
commit 2e9a0ebfb1
3 changed files with 213 additions and 23 deletions
+46
View File
@@ -0,0 +1,46 @@
'use strict';
/* db-maintain.js — обслуживание SQLite-БД: проверка целостности + компактизация.
* Шаги: PRAGMA integrity_check -> PRAGMA wal_checkpoint(TRUNCATE) -> VACUUM.
* Путь к БД: env DB_PATH, либо argv[2], либо стандартный.
* VACUUM требует, чтобы БД никто не писал — запускать на ОСТАНОВЛЕННОМ сервере.
* Используется панелью управления (control-panel.ps1, пункт «Обслуживание БД»).
*/
const path = require('path');
const fs = require('fs');
const { DatabaseSync } = require('node:sqlite');
const DB = process.env.DB_PATH || process.argv[2] || path.join(__dirname, '..', 'data', 'learnspace.db');
function sizeMB(p) {
try { return (fs.statSync(p).size / 1048576).toFixed(1) + ' МБ'; } catch (_) { return '?'; }
}
if (!fs.existsSync(DB)) { console.error('БД не найдена: ' + DB); process.exit(1); }
const before = sizeMB(DB);
const db = new DatabaseSync(DB);
try { db.exec('PRAGMA busy_timeout=8000'); } catch (_) {}
// 1) Проверка целостности
let integ = '?';
try {
const rows = db.prepare('PRAGMA integrity_check').all();
integ = rows.map(r => (r.integrity_check !== undefined ? r.integrity_check : Object.values(r)[0])).join('; ');
} catch (e) { integ = 'ошибка: ' + e.message; }
const integOk = (integ === 'ok');
console.log('Целостность: ' + (integOk ? 'ok' : integ));
// 2) Сброс WAL в основной файл
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); console.log('WAL checkpoint: ok'); }
catch (e) { console.log('WAL checkpoint: ' + e.message); }
// 3) Компактизация (только если целостность в порядке — VACUUM на битой БД опасен)
if (integOk) {
try { db.exec('VACUUM'); console.log('VACUUM: ok'); }
catch (e) { console.log('VACUUM: ' + e.message); }
} else {
console.log('VACUUM пропущен: сначала восстановите целостность (откат из бэкапа).');
}
db.close();
console.log('Размер БД: ' + before + ' -> ' + sizeMB(DB));