import Database from 'better-sqlite3'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; import bcrypt from 'bcryptjs'; import { existsSync, mkdirSync } from 'fs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DB_PATH = __dirname + '/data/hospital.db'; // Ensure data directory exists if (!existsSync(__dirname + '/data')) { mkdirSync(__dirname + '/data', { recursive: true }); } // Create persistent database connection with WAL mode and proper settings const db = new Database(DB_PATH); // Enable WAL mode for better concurrency db.pragma('journal_mode = WAL'); // Set busy timeout to 5000ms (5 seconds) db.pragma('busy_timeout = 5000'); // Additional performance and concurrency settings db.pragma('synchronous = NORMAL'); db.pragma('cache_size = 1000'); db.pragma('temp_store = MEMORY'); db.pragma('mmap_size = 268435456'); // 256MB export function getDb() { return db; } function rowToObj(row) { if (!row) return row; const obj = {}; for (const key in row) { const val = row[key]; try { obj[key] = typeof val === 'string' && (val.startsWith('[') || val.startsWith('{')) ? JSON.parse(val) : val; } catch { obj[key] = val; } } return obj; } export function getValue(key) { const row = db.prepare('SELECT value FROM kv WHERE key = ?').get(key); return row?.value ?? null; } export function setValue(key, value) { const stmt = db.prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value'); stmt.run(key, value); } export function getUsuarioByDni(dni) { const row = db.prepare('SELECT * FROM usuarios WHERE dni = ?').get(dni); return row || null; } export function getUsuarioById(id) { const row = db.prepare('SELECT * FROM usuarios WHERE id = ?').get(id); return row || null; } export function getAllUsuarios() { const rows = db.prepare('SELECT * FROM usuarios ORDER BY apellido, nombre').all(); return rows; } export function createUsuario(usuario) { const stmt = db.prepare(`INSERT INTO usuarios (id, apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, passwordHash, areaId, fechaCreacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`); stmt.run( usuario.id, usuario.apellido, usuario.nombre, usuario.dni, usuario.fechaNacimiento, usuario.email, usuario.rol, usuario.matriculaProfesional || null, usuario.passwordHash, usuario.areaId || null, usuario.fechaCreacion ); } export function updateUsuario(id, datos) { const fields = []; const values = []; if (datos.apellido !== undefined) { fields.push('apellido = ?'); values.push(datos.apellido); } if (datos.nombre !== undefined) { fields.push('nombre = ?'); values.push(datos.nombre); } if (datos.fechaNacimiento !== undefined) { fields.push('fechaNacimiento = ?'); values.push(datos.fechaNacimiento); } if (datos.email !== undefined) { fields.push('email = ?'); values.push(datos.email); } if (datos.rol !== undefined) { fields.push('rol = ?'); values.push(datos.rol); } if (datos.matriculaProfesional !== undefined) { fields.push('matriculaProfesional = ?'); values.push(datos.matriculaProfesional); } if (datos.passwordHash !== undefined) { fields.push('passwordHash = ?'); values.push(datos.passwordHash); } if (datos.areaId !== undefined) { fields.push('areaId = ?'); values.push(datos.areaId); } if (fields.length > 0) { values.push(id); db.prepare(`UPDATE usuarios SET ${fields.join(', ')} WHERE id = ?`).run(values); } } export function deleteUsuario(id) { db.prepare('DELETE FROM usuarios WHERE id = ?').run(id); } export function verifyPassword(dni, password) { const row = db.prepare('SELECT passwordHash FROM usuarios WHERE dni = ?').get(dni); if (!row) return false; return bcrypt.compareSync(password, row.passwordHash); } export function hashPassword(password) { return bcrypt.hashSync(password, 10); } export function getAllPacientes() { const rows = db.prepare('SELECT * FROM pacientes').all(); return rows; } export function getAllAreas() { const rows = db.prepare('SELECT * FROM areas').all(); return rows; } export function getAllCamas() { const rows = db.prepare('SELECT * FROM camas').all(); return rows; } export function updateCama(id, updates) { const fields = []; const values = []; if (updates.estado !== undefined) { fields.push('estado = ?'); values.push(updates.estado); } if (updates.pacienteId !== undefined) { fields.push('pacienteId = ?'); values.push(updates.pacienteId); } if (updates.internacionId !== undefined) { fields.push('internacionId = ?'); values.push(updates.internacionId); } if (fields.length > 0) { values.push(id); db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(values); } } export function getAllInternaciones() { const rows = db.prepare('SELECT * FROM internaciones').all(); return rows.map(i => ({ ...i, activa: !!i.activa })); } export function getAllEvoluciones() { const rows = db.prepare('SELECT * FROM evoluciones').all(); return rows.map(e => ({ ...e, signosVitales: typeof e.signosVitales === 'string' ? JSON.parse(e.signosVitales) : e.signosVitales, examenFisico: typeof e.examenFisico === 'string' ? JSON.parse(e.examenFisico) : e.examenFisico })); } export function getAllLaboratorios() { const rows = db.prepare('SELECT * FROM laboratorios').all(); return rows.map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })); } export function getAllAcidosBase() { const rows = db.prepare('SELECT * FROM acidosbase').all(); return rows; } export function getAllCultivos() { const rows = db.prepare('SELECT * FROM cultivos').all(); return rows; } export function getAllEstudiosComplementarios() { const rows = db.prepare('SELECT * FROM estudiosComplementarios').all(); return rows; } export function getAllInterconsultas() { const rows = db.prepare('SELECT * FROM interconsultas').all(); return rows.map(ic => ({ ...ic, realizada: !!ic.realizada })); } export function getAllAtb() { const rows = db.prepare('SELECT * FROM atb').all(); return rows; } export function getAllIndicaciones() { const rows = db.prepare('SELECT * FROM indicaciones').all(); return rows; } export function getAllMovimientosIndicaciones() { const rows = db.prepare('SELECT * FROM movimientos_indicaciones').all(); return rows; }