Fix: solucionar error SQLITE_BUSY y mejorar backend
- Migrar de sqlite3 a better-sqlite3 con conexión persistente - Configurar WAL mode, busy_timeout y PRAGMAs optimizados - Implementar transacciones para operaciones bulk - Corregir error initialLoadComplete en useHospitalStore.ts - Agregar tablas usuarios y kv faltantes - Actualizar .gitignore para archivos WAL de SQLite
This commit is contained in:
+100
-136
@@ -1,83 +1,79 @@
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { open } from 'sqlite';
|
||||
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';
|
||||
|
||||
export async function openDb() {
|
||||
const db = await open({
|
||||
filename: DB_PATH,
|
||||
driver: sqlite3.Database,
|
||||
});
|
||||
// Ensure data directory exists
|
||||
if (!existsSync(__dirname + '/data')) {
|
||||
mkdirSync(__dirname + '/data', { recursive: true });
|
||||
}
|
||||
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS kv (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
`);
|
||||
// Create persistent database connection with WAL mode and proper settings
|
||||
const db = new Database(DB_PATH);
|
||||
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS usuarios (
|
||||
id TEXT PRIMARY KEY,
|
||||
apellido TEXT NOT NULL,
|
||||
nombre TEXT NOT NULL,
|
||||
dni TEXT UNIQUE NOT NULL,
|
||||
fechaNacimiento TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
rol TEXT NOT NULL CHECK(rol IN ('admin', 'medico', 'enfermero')),
|
||||
matriculaProfesional TEXT,
|
||||
passwordHash TEXT NOT NULL,
|
||||
areaId TEXT,
|
||||
fechaCreacion TEXT NOT NULL
|
||||
);
|
||||
`);
|
||||
// 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;
|
||||
}
|
||||
|
||||
export async function getValue(key) {
|
||||
const db = await openDb();
|
||||
const row = await db.get('SELECT value FROM kv WHERE key = ?', key);
|
||||
await db.close();
|
||||
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 async function setValue(key, value) {
|
||||
const db = await openDb();
|
||||
await db.run('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value', key, value);
|
||||
await db.close();
|
||||
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 async function getUsuarioByDni(dni) {
|
||||
const db = await openDb();
|
||||
const row = await db.get('SELECT * FROM usuarios WHERE dni = ?', dni);
|
||||
await db.close();
|
||||
export function getUsuarioByDni(dni) {
|
||||
const row = db.prepare('SELECT * FROM usuarios WHERE dni = ?').get(dni);
|
||||
return row || null;
|
||||
}
|
||||
|
||||
export async function getUsuarioById(id) {
|
||||
const db = await openDb();
|
||||
const row = await db.get('SELECT * FROM usuarios WHERE id = ?', id);
|
||||
await db.close();
|
||||
export function getUsuarioById(id) {
|
||||
const row = db.prepare('SELECT * FROM usuarios WHERE id = ?').get(id);
|
||||
return row || null;
|
||||
}
|
||||
|
||||
export async function getAllUsuarios() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM usuarios ORDER BY apellido, nombre');
|
||||
await db.close();
|
||||
export function getAllUsuarios() {
|
||||
const rows = db.prepare('SELECT * FROM usuarios ORDER BY apellido, nombre').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function createUsuario(usuario) {
|
||||
const db = await openDb();
|
||||
await db.run(
|
||||
`INSERT INTO usuarios (id, apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, passwordHash, areaId, fechaCreacion)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
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,
|
||||
@@ -90,11 +86,9 @@ export async function createUsuario(usuario) {
|
||||
usuario.areaId || null,
|
||||
usuario.fechaCreacion
|
||||
);
|
||||
await db.close();
|
||||
}
|
||||
|
||||
export async function updateUsuario(id, datos) {
|
||||
const db = await openDb();
|
||||
export function updateUsuario(id, datos) {
|
||||
const fields = [];
|
||||
const values = [];
|
||||
|
||||
@@ -109,53 +103,40 @@ export async function updateUsuario(id, datos) {
|
||||
|
||||
if (fields.length > 0) {
|
||||
values.push(id);
|
||||
await db.run(`UPDATE usuarios SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
db.prepare(`UPDATE usuarios SET ${fields.join(', ')} WHERE id = ?`).run(values);
|
||||
}
|
||||
await db.close();
|
||||
}
|
||||
|
||||
export async function deleteUsuario(id) {
|
||||
const db = await openDb();
|
||||
await db.run('DELETE FROM usuarios WHERE id = ?', id);
|
||||
await db.close();
|
||||
export function deleteUsuario(id) {
|
||||
db.prepare('DELETE FROM usuarios WHERE id = ?').run(id);
|
||||
}
|
||||
|
||||
export async function verifyPassword(dni, password) {
|
||||
const db = await openDb();
|
||||
const row = await db.get('SELECT passwordHash FROM usuarios WHERE dni = ?', dni);
|
||||
await db.close();
|
||||
|
||||
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 async function hashPassword(password) {
|
||||
export function hashPassword(password) {
|
||||
return bcrypt.hashSync(password, 10);
|
||||
}
|
||||
|
||||
export async function getAllPacientes() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM pacientes');
|
||||
await db.close();
|
||||
export function getAllPacientes() {
|
||||
const rows = db.prepare('SELECT * FROM pacientes').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllAreas() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM areas');
|
||||
await db.close();
|
||||
export function getAllAreas() {
|
||||
const rows = db.prepare('SELECT * FROM areas').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllCamas() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM camas');
|
||||
await db.close();
|
||||
export function getAllCamas() {
|
||||
const rows = db.prepare('SELECT * FROM camas').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function updateCama(id, updates) {
|
||||
const db = await openDb();
|
||||
export function updateCama(id, updates) {
|
||||
const fields = [];
|
||||
const values = [];
|
||||
if (updates.estado !== undefined) {
|
||||
@@ -172,77 +153,60 @@ export async function updateCama(id, updates) {
|
||||
}
|
||||
if (fields.length > 0) {
|
||||
values.push(id);
|
||||
await db.run(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
db.prepare(`UPDATE camas SET ${fields.join(', ')} WHERE id = ?`).run(values);
|
||||
}
|
||||
await db.close();
|
||||
}
|
||||
|
||||
export async function getAllInternaciones() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM internaciones');
|
||||
await db.close();
|
||||
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 async function getAllEvoluciones() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM evoluciones');
|
||||
await db.close();
|
||||
export function getAllCultivos() {
|
||||
const rows = db.prepare('SELECT * FROM cultivos').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllLaboratorios() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM laboratorios');
|
||||
await db.close();
|
||||
export function getAllEstudiosComplementarios() {
|
||||
const rows = db.prepare('SELECT * FROM estudiosComplementarios').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllAcidosBase() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM acidosbase');
|
||||
await db.close();
|
||||
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 async function getAllCultivos() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM cultivos');
|
||||
await db.close();
|
||||
export function getAllIndicaciones() {
|
||||
const rows = db.prepare('SELECT * FROM indicaciones').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllEstudiosComplementarios() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM estudiosComplementarios');
|
||||
await db.close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllInterconsultas() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM interconsultas');
|
||||
await db.close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllAtb() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM atb');
|
||||
await db.close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllIndicaciones() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM indicaciones');
|
||||
await db.close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function getAllMovimientosIndicaciones() {
|
||||
const db = await openDb();
|
||||
const rows = await db.all('SELECT * FROM movimientos_indicaciones');
|
||||
await db.close();
|
||||
export function getAllMovimientosIndicaciones() {
|
||||
const rows = db.prepare('SELECT * FROM movimientos_indicaciones').all();
|
||||
return rows;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user