Files
Learn_System/backend/scripts/reset-system.js
T
Maxim Dolgolyov 22c7b38e9a feat(admin): сброс системы «чистый запуск» в веб-панели
Добавлено такое же действие, как [Z] в control-panel: POST /api/admin/reset-system
(+ /reset-system/plan для предпросмотра), только admin. Общая логика вынесена в
src/services/systemReset.js (classify/pickKeptAdmin/runReset) — реюзится CLI и эндпоинтом.

Веб-эндпоинт безопаснее CLI: сохраняет ТЕКУЩЕГО админа (оператор остаётся залогинен),
делает бэкап БД ДО сброса (wal_checkpoint + копия в data/backups/), требует body.confirm='СБРОС'.
UI — «Опасная зона» в overview-секции: предпросмотр плана + ввод «СБРОС» + результат с именем бэкапа.

db.js: добавлен db._path (нужен бэкапу при сбросе). Логика проверена смоуком на копии живой БД
(16 юзеров удалено, контент сохранён, REASSIGN на админа, гейм-счётчики обнулены, 0 висячих FK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:45:13 +03:00

62 lines
4.0 KiB
JavaScript

'use strict';
/* ───────────────────────────────────────────────────────────────────────────
reset-system.js — CLI «ЧИСТЫЙ ЗАПУСК» (тонкая обёртка над src/services/systemReset.js).
⚠️ ДЕСТРУКТИВНО. По умолчанию DRY-RUN. Выполнение — только с --apply --confirm=RESET.
Перед сбросом сделайте бэкап (control-panel «Бэкап БД» делает автоматически).
Та же логика доступна в админ-веб-панели (POST /api/admin/reset-system).
Запуск:
node backend/scripts/reset-system.js # план
node backend/scripts/reset-system.js --apply --confirm=RESET # выполнить
─────────────────────────────────────────────────────────────────────────── */
const { DatabaseSync } = require('node:sqlite');
const path = require('path');
const reset = require('../src/services/systemReset');
const APPLY = process.argv.includes('--apply');
const CONFIRM = process.argv.includes('--confirm=RESET');
const DB = path.join(__dirname, '..', 'data', 'learnspace.db');
const db = new DatabaseSync(DB);
const keptAdmin = reset.pickKeptAdmin(db);
if (!keptAdmin) {
console.error('✗ В системе нет ни одного админа — сброс отменён (иначе залочитесь). Создайте админа сначала.');
db.close(); process.exit(1);
}
const plan = reset.classify(db);
console.log(`\n=== reset-system «ЧИСТЫЙ ЗАПУСК» (${APPLY ? (CONFIRM ? 'APPLY' : 'нужен --confirm=RESET') : 'DRY-RUN'}) ===`);
console.log(`Сохраняемый админ: id=${keptAdmin.id} ${keptAdmin.email} «${keptAdmin.name}»`);
console.log(`Пользователей: ${plan.totalUsers} → останется 1, удалится ${plan.totalUsers - 1}\n`);
console.log('REASSIGN (контент → админу):');
plan.reassign.forEach(r => console.log(` ${r.table.padEnd(22)} ${r.col.padEnd(12)} строк: ${r.rows}`));
console.log('\nWIPE (полная очистка):');
plan.wipe.forEach(w => console.log(` ${w.table.padEnd(28)} строк: ${w.rows}`));
console.log(` — всего к удалению (без каскада users): ~${plan.wipeRows}`);
console.log(`\nKEEP (контент/конфиг): ${plan.keepCount} таблиц.`);
if (plan.unknown.length) console.log(`\n⚠️ НЕИЗВЕСТНЫЕ таблицы (НЕ трогаем): ${plan.unknown.join(', ')}`);
if (!APPLY) {
console.log('\nDRY-RUN: ничего не изменено. Выполнить: node backend/scripts/reset-system.js --apply --confirm=RESET\n');
db.close(); process.exit(0);
}
if (!CONFIRM) {
console.error('\n✗ Нужен флаг --confirm=RESET (защита от случайного запуска). Отмена.');
db.close(); process.exit(1);
}
try {
const res = reset.runReset(db, keptAdmin.id);
console.log(`\n✓ ЧИСТЫЙ ЗАПУСК выполнен. Удалено пользователей: ${res.deletedUsers}, осталось: ${res.remainingUsers}.`);
console.log(`✓ Контент сохранён: учебники ${res.kept.textbooks}, вопросы ${res.kept.questions}, тесты ${res.kept.tests}, курсы ${res.kept.courses}, exam-prep ${res.kept.exam_tasks}.`);
if (res.fkDangling) console.log(`⚠️ foreign_key_check: ${res.fkDangling} висячих ссылок — проверьте.`);
console.log(`\nВойдите под ${keptAdmin.email}. Перезапустите сервер.\n`);
} catch (e) {
console.error('\n✗ Ошибка — откат, изменений нет:', e.message);
db.close(); process.exit(1);
}
db.close();