const initSqlJs = require('sql.js'); const path = require('path'); const fs = require('fs'); const DATA_DIR = path.join(__dirname, '..', 'data'); const DB_PATH = path.join(DATA_DIR, 'exams.db'); let db = null; async function initDb() { if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } const SQL = await initSqlJs(); if (fs.existsSync(DB_PATH)) { const buffer = fs.readFileSync(DB_PATH); db = new SQL.Database(buffer); } else { db = new SQL.Database(); } db.run('PRAGMA foreign_keys = ON'); db.run(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT UNIQUE NOT NULL, password TEXT, name TEXT NOT NULL, email TEXT, role TEXT DEFAULT 'teacher', auth_type TEXT DEFAULT 'local', groups TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_login DATETIME ) `); db.run(` CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, last_name TEXT NOT NULL, first_name TEXT NOT NULL, patronymic TEXT DEFAULT '', class TEXT DEFAULT '', school_code TEXT DEFAULT '', passport_series TEXT DEFAULT '', passport_number TEXT DEFAULT '', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(last_name, first_name, patronymic, class) ) `); db.run(` CREATE TABLE IF NOT EXISTS subjects ( code TEXT PRIMARY KEY, name TEXT NOT NULL, exam_date TEXT DEFAULT '', is_mandatory INTEGER DEFAULT 0 ) `); db.run(` CREATE TABLE IF NOT EXISTS exam_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id INTEGER NOT NULL, subject_code TEXT NOT NULL, primary_score INTEGER, grade INTEGER, FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE, FOREIGN KEY (subject_code) REFERENCES subjects(code), UNIQUE(student_id, subject_code) ) `); db.run('INSERT OR IGNORE INTO subjects (code, name, is_mandatory) VALUES (?, ?, 1)', ['01', 'Русский язык']); db.run('INSERT OR IGNORE INTO subjects (code, name, is_mandatory) VALUES (?, ?, 1)', ['02', 'Математика']); saveDb(); console.log('БД инициализирована'); return db; } function saveDb() { if (!db) return; const data = db.export(); const buffer = Buffer.from(data); fs.writeFileSync(DB_PATH, buffer); } function getDb() { if (!db) throw new Error('БД не инициализирована.'); return db; } // Wrappers emulating better-sqlite3 API function dbGet(sql, params = []) { const st = db.prepare(sql); st.bind(params); const hasRow = st.step(); const row = hasRow ? st.getAsObject() : null; st.free(); return row; } function dbAll(sql, params = []) { const st = db.prepare(sql); st.bind(params); const rows = []; while (st.step()) { rows.push(st.getAsObject()); } st.free(); return rows; } function dbRun(sql, params = []) { const before = db.getRowsModified(); db.run(sql, params); const after = db.getRowsModified(); return { changes: after - before }; } function dbLastInsertId() { const r = db.exec('SELECT last_insert_rowid() as id'); return r.length ? r[0].values[0][0] : null; } function dbExec(sql) { db.run(sql); } function dbBegin() { db.run('BEGIN'); } function dbCommit() { db.run('COMMIT'); } function dbRollback() { try { db.run('ROLLBACK'); } catch (e) {} } module.exports = { initDb, getDb, saveDb, dbGet, dbAll, dbRun, dbLastInsertId, dbExec, dbBegin, dbCommit, dbRollback };