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:
2026-04-24 02:06:21 -03:00
parent d30dbab8f0
commit e887d66411
8 changed files with 405 additions and 327 deletions
+206 -166
View File
@@ -1,6 +1,6 @@
import express from 'express';
import cors from 'cors';
import { openDb, getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword, getAllPacientes, getAllAreas, getAllCamas, getAllInternaciones, getAllEvoluciones, getAllLaboratorios, getAllAcidosBase, getAllCultivos, getAllEstudiosComplementarios, getAllInterconsultas, getAllAtb, getAllIndicaciones, getAllMovimientosIndicaciones } from './db.js';
import { getDb, getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword, getAllPacientes, getAllAreas, getAllCamas, getAllInternaciones, getAllEvoluciones, getAllLaboratorios, getAllAcidosBase, getAllCultivos, getAllEstudiosComplementarios, getAllInterconsultas, getAllAtb, getAllIndicaciones, getAllMovimientosIndicaciones } from './db.js';
const app = express();
app.use(cors());
@@ -9,26 +9,22 @@ app.use(express.json({ limit: '5mb' }));
const PORT = process.env.PORT || 4001;
const STORAGE_KEY = 'hospital-data-v1';
app.get('/api/state', async (req, res) => {
app.get('/api/state', (req, res) => {
try {
const state = {
pacientes: await getAllPacientes(),
areas: await getAllAreas(),
camas: await getAllCamas(),
internaciones: (await getAllInternaciones()).map(i => ({ ...i, activa: !!i.activa })),
evoluciones: (await getAllEvoluciones()).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
})),
laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),
acidosBase: await getAllAcidosBase(),
cultivos: await getAllCultivos(),
estudiosComplementarios: await getAllEstudiosComplementarios(),
interconsultas: (await getAllInterconsultas()).map(ic => ({ ...ic, realizada: !!ic.realizada })),
atb: await getAllAtb(),
indicaciones: await getAllIndicaciones(),
movimientosIndicaciones: await getAllMovimientosIndicaciones(),
pacientes: getAllPacientes(),
areas: getAllAreas(),
camas: getAllCamas(),
internaciones: getAllInternaciones(),
evoluciones: getAllEvoluciones(),
laboratorios: getAllLaboratorios(),
acidosBase: getAllAcidosBase(),
cultivos: getAllCultivos(),
estudiosComplementarios: getAllEstudiosComplementarios(),
interconsultas: getAllInterconsultas(),
atb: getAllAtb(),
indicaciones: getAllIndicaciones(),
movimientosIndicaciones: getAllMovimientosIndicaciones(),
vistaActual: 'dashboard',
currentInternacionId: null
};
@@ -39,137 +35,119 @@ app.get('/api/state', async (req, res) => {
}
});
app.put('/api/state', async (req, res) => {
app.put('/api/state', (req, res) => {
try {
const state = req.body;
const db = await openDb();
const db = getDb();
await db.run('DELETE FROM evoluciones');
await db.run('DELETE FROM acidosbase');
await db.run('DELETE FROM cultivos');
await db.run('DELETE FROM laboratorios');
await db.run('DELETE FROM internaciones');
await db.run('DELETE FROM camas');
await db.run('DELETE FROM areas');
await db.run('DELETE FROM pacientes');
await db.run('DELETE FROM estudiosComplementarios');
await db.run('DELETE FROM interconsultas');
await db.run('DELETE FROM atb');
await db.run('DELETE FROM indicaciones');
await db.run('DELETE FROM movimientos_indicaciones');
// Use transaction for atomicity and better performance
const result = db.transaction(() => {
db.prepare('DELETE FROM evoluciones').run();
db.prepare('DELETE FROM acidosbase').run();
db.prepare('DELETE FROM cultivos').run();
db.prepare('DELETE FROM laboratorios').run();
db.prepare('DELETE FROM internaciones').run();
db.prepare('DELETE FROM camas').run();
db.prepare('DELETE FROM areas').run();
db.prepare('DELETE FROM pacientes').run();
db.prepare('DELETE FROM estudiosComplementarios').run();
db.prepare('DELETE FROM interconsultas').run();
db.prepare('DELETE FROM atb').run();
db.prepare('DELETE FROM indicaciones').run();
db.prepare('DELETE FROM movimientos_indicaciones').run();
if (state.pacientes?.length) {
for (const p of state.pacientes) {
await db.run(
'INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[p.id, p.apellido, p.nombre, p.dni, p.fechaNacimiento || null, p.sexo || null, p.telefono || null, p.email || null, p.direccion || null, p.obraSocial || null, p.nacionalidad || null, p.medicacionHabitual || null, p.antecedentes || null, p.alergias || null, p.grupoSanguineo || null, p.historiaClinica || null]
);
if (state.pacientes?.length) {
const stmt = db.prepare('INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const p of state.pacientes) {
stmt.run(p.id, p.apellido, p.nombre, p.dni, p.fechaNacimiento || null, p.sexo || null, p.telefono || null, p.email || null, p.direccion || null, p.obraSocial || null, p.nacionalidad || null, p.medicacionHabitual || null, p.antecedentes || null, p.alergias || null, p.grupoSanguineo || null, p.historiaClinica || null);
}
}
}
if (state.areas?.length) {
for (const a of state.areas) {
await db.run('INSERT INTO areas (id, nombre) VALUES (?, ?)', [a.id, a.nombre]);
if (state.areas?.length) {
const stmt = db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)');
for (const a of state.areas) {
stmt.run(a.id, a.nombre);
}
}
}
if (state.camas?.length) {
for (const c of state.camas) {
await db.run('INSERT INTO camas (id, numero, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)', [c.id, c.numero, c.areaId, c.tipo, c.estado]);
if (state.camas?.length) {
const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)');
for (const c of state.camas) {
stmt.run(c.id, c.numero, c.areaId, c.tipo, c.estado);
}
}
}
if (state.internaciones?.length) {
for (const i of state.internaciones) {
await db.run(
'INSERT OR IGNORE INTO internaciones (id, pacienteId, camaId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[i.id, i.pacienteId, i.camaId, i.areaId || null, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null]
);
if (state.internaciones?.length) {
const stmt = db.prepare('INSERT OR IGNORE INTO internaciones (id, pacienteId, camaId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const i of state.internaciones) {
stmt.run(i.id, i.pacienteId, i.camaId, i.areaId || null, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null);
}
}
}
if (state.evoluciones?.length) {
for (const e of state.evoluciones) {
await db.run(
'INSERT INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[e.id, e.internacionId, e.fecha, e.hora || '', e.medico || '', JSON.stringify(e.signosVitales || {}), JSON.stringify(e.examenFisico || {}), e.novedades || '', e.comentarios || '', e.pendientes || '']
);
if (state.evoluciones?.length) {
const stmt = db.prepare('INSERT OR IGNORE INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const e of state.evoluciones) {
stmt.run(e.id, e.internacionId, e.fecha, e.hora || '', e.medico || '', JSON.stringify(e.signosVitales || {}), JSON.stringify(e.examenFisico || {}), e.novedades || '', e.comentarios || '', e.pendientes || '');
}
}
}
if (state.laboratorios?.length) {
for (const l of state.laboratorios) {
await db.run(
'INSERT INTO laboratorios (id, pacienteId, fecha, hora, tipo, resultados, observaciones) VALUES (?, ?, ?, ?, ?, ?, ?)',
[l.id, l.pacienteId, l.fecha, l.hora || null, l.tipo, JSON.stringify(l.resultados), l.observaciones || null]
);
if (state.laboratorios?.length) {
const stmt = db.prepare('INSERT INTO laboratorios (id, pacienteId, fecha, hora, tipo, resultados, observaciones) VALUES (?, ?, ?, ?, ?, ?, ?)');
for (const l of state.laboratorios) {
stmt.run(l.id, l.pacienteId, l.fecha, l.hora || null, l.tipo, JSON.stringify(l.resultados), l.observaciones || null);
}
}
}
if (state.acidosBase?.length) {
for (const a of state.acidosBase) {
await db.run(
'INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, sato2, lactato, interpretacion, fio2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[a.id, a.pacienteId, a.fecha, a.hora, a.ph, a.pco2, a.po2, a.hco3, a.be, a.sato2, a.lactato || null, a.interpretacion || null, a.fio2 || null]
);
if (state.acidosBase?.length) {
const stmt = db.prepare('INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, sato2, lactato, interpretacion, fio2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const a of state.acidosBase) {
stmt.run(a.id, a.pacienteId, a.fecha, a.hora, a.ph, a.pco2, a.po2, a.hco3, a.be, a.sato2, a.lactato || null, a.interpretacion || null, a.fio2 || null);
}
}
}
if (state.cultivos?.length) {
for (const c of state.cultivos) {
await db.run(
'INSERT INTO cultivos (id, pacienteId, internacionId, fechaToma, protocolo, tipoMuestra, observaciones, estado, fechaResultado, germen, sensible, resistente) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[c.id, c.pacienteId, c.internacionId, c.fechaToma, c.protocolo, c.tipoMuestra, c.observaciones, c.estado, c.fechaResultado, c.germen, c.sensible, c.resistente]
);
if (state.cultivos?.length) {
const stmt = db.prepare('INSERT INTO cultivos (id, pacienteId, internacionId, fechaToma, protocolo, tipoMuestra, observaciones, estado, fechaResultado, germen, sensible, resistente) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const c of state.cultivos) {
stmt.run(c.id, c.pacienteId, c.internacionId, c.fechaToma, c.protocolo, c.tipoMuestra, c.observaciones, c.estado, c.fechaResultado, c.germen, c.sensible, c.resistente);
}
}
}
if (state.estudiosComplementarios?.length) {
for (const e of state.estudiosComplementarios) {
await db.run(
'INSERT INTO estudiosComplementarios (id, pacienteId, internacionId, fecha, tipo, resultado) VALUES (?, ?, ?, ?, ?, ?)',
[e.id, e.pacienteId, e.internacionId || null, e.fecha, e.tipo, e.resultado]
);
if (state.estudiosComplementarios?.length) {
const stmt = db.prepare('INSERT INTO estudiosComplementarios (id, pacienteId, internacionId, fecha, tipo, resultado) VALUES (?, ?, ?, ?, ?, ?)');
for (const e of state.estudiosComplementarios) {
stmt.run(e.id, e.pacienteId, e.internacionId || null, e.fecha, e.tipo, e.resultado);
}
}
}
if (state.interconsultas?.length) {
for (const ic of state.interconsultas) {
await db.run(
'INSERT INTO interconsultas (id, pacienteId, internacionId, fecha, servicioInterconsultado, motivo, respuestaInterconsulta, respuestaFecha) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[ic.id, ic.pacienteId, ic.internacionId, ic.fecha, ic.servicioInterconsultado, ic.motivo || null, ic.respuestaInterconsulta || null, ic.respuestaFecha || null]
);
if (state.interconsultas?.length) {
const stmt = db.prepare('INSERT INTO interconsultas (id, pacienteId, internacionId, fecha, servicioInterconsultado, motivo, respuestaInterconsulta, respuestaFecha) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
for (const ic of state.interconsultas) {
stmt.run(ic.id, ic.pacienteId, ic.internacionId, ic.fecha, ic.servicioInterconsultado, ic.motivo || null, ic.respuestaInterconsulta || null, ic.respuestaFecha || null);
}
}
}
if (state.atb?.length) {
for (const atb of state.atb) {
await db.run(
'INSERT INTO atb (id, pacienteId, internacionId, antibiotico, fechaInicio, fechaFinalizacion) VALUES (?, ?, ?, ?, ?, ?)',
[atb.id, atb.pacienteId, atb.internacionId, atb.antibiotico, atb.fechaInicio, atb.fechaFinalizacion || null]
);
if (state.atb?.length) {
const stmt = db.prepare('INSERT INTO atb (id, pacienteId, internacionId, antibiotico, fechaInicio, fechaFinalizacion) VALUES (?, ?, ?, ?, ?, ?)');
for (const atb of state.atb) {
stmt.run(atb.id, atb.pacienteId, atb.internacionId, atb.antibiotico, atb.fechaInicio, atb.fechaFinalizacion || null);
}
}
}
if (state.indicaciones?.length) {
for (const ind of state.indicaciones) {
await db.run(
'INSERT INTO indicaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche, indicacionNoFco) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[ind.id, ind.internacionId, ind.tipo, ind.droga || null, ind.dosis || null, ind.frecuenciaHoras || null, ind.via || null, ind.tipoPlan || null, ind.tipoPlan2 || null, ind.cantidadMl || null, ind.cantidadMl2 || null, ind.tiempoHoras || null, ind.estado, ind.medicoCrea, ind.fechaCrea, ind.tipoInsulina || null, ind.unidadesDesayuno || null, ind.unidadesAlmuerzo || null, ind.unidadesNoche || null, ind.indicacionNoFco || null]
);
if (state.indicaciones?.length) {
const stmt = db.prepare('INSERT INTO indicaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche, indicacionNoFco) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const ind of state.indicaciones) {
stmt.run(ind.id, ind.internacionId, ind.tipo, ind.droga || null, ind.dosis || null, ind.frecuenciaHoras || null, ind.via || null, ind.tipoPlan || null, ind.tipoPlan2 || null, ind.cantidadMl || null, ind.cantidadMl2 || null, ind.tiempoHoras || null, ind.estado, ind.medicoCrea, ind.fechaCrea, ind.tipoInsulina || null, ind.unidadesDesayuno || null, ind.unidadesAlmuerzo || null, ind.unidadesNoche || null, ind.indicacionNoFco || null);
}
}
}
if (state.movimientosIndicaciones?.length) {
for (const mov of state.movimientosIndicaciones) {
await db.run(
'INSERT INTO movimientos_indicaciones (id, indicacionId, internacionId, tipo, fecha, profesional, indicacionPrevia, indicacionNueva) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[mov.id, mov.indicacionId, mov.internacionId, mov.tipo, mov.fecha, mov.profesional, mov.indicacionPrevia || null, mov.indicacionNueva || null]
);
if (state.movimientosIndicaciones?.length) {
const stmt = db.prepare('INSERT INTO movimientos_indicaciones (id, indicacionId, internacionId, tipo, fecha, profesional, indicacionPrevia, indicacionNueva) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
for (const mov of state.movimientosIndicaciones) {
stmt.run(mov.id, mov.indicacionId, mov.internacionId, mov.tipo, mov.fecha, mov.profesional, mov.indicacionPrevia || null, mov.indicacionNueva || null);
}
}
}
})();
await db.close();
res.json({ ok: true });
} catch (err) {
console.error(err);
@@ -177,8 +155,79 @@ app.put('/api/state', async (req, res) => {
}
});
// Partial state update (only changed parts)
app.put('/api/state/partial', (req, res) => {
try {
const updates = req.body;
if (!updates || typeof updates !== 'object') {
return res.status(400).json({ error: 'Invalid update data' });
}
const db = getDb();
// Use transaction for atomicity
const result = db.transaction(() => {
// Only update tables that were actually changed
if (updates.pacientes) {
db.prepare('DELETE FROM pacientes').run();
if (updates.pacientes.length > 0) {
const stmt = db.prepare('INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const p of updates.pacientes) {
stmt.run(p.id, p.apellido, p.nombre, p.dni, p.fechaNacimiento || null, p.sexo || null, p.telefono || null, p.email || null, p.direccion || null, p.obraSocial || null, p.nacionalidad || null, p.medicacionHabitual || null, p.antecedentes || null, p.alergias || null, p.grupoSanguineo || null, p.historiaClinica || null);
}
}
}
if (updates.areas) {
db.prepare('DELETE FROM areas').run();
if (updates.areas.length > 0) {
const stmt = db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)');
for (const a of updates.areas) {
stmt.run(a.id, a.nombre);
}
}
}
if (updates.camas) {
db.prepare('DELETE FROM camas').run();
if (updates.camas.length > 0) {
const stmt = db.prepare('INSERT INTO camas (id, numero, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)');
for (const c of updates.camas) {
stmt.run(c.id, c.numero, c.areaId, c.tipo, c.estado);
}
}
}
if (updates.internaciones) {
db.prepare('DELETE FROM internaciones').run();
if (updates.internaciones.length > 0) {
const stmt = db.prepare('INSERT OR IGNORE INTO internaciones (id, pacienteId, camaId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const i of updates.internaciones) {
stmt.run(i.id, i.pacienteId, i.camaId, i.areaId || null, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null);
}
}
}
if (updates.evoluciones) {
db.prepare('DELETE FROM evoluciones').run();
if (updates.evoluciones.length > 0) {
const stmt = db.prepare('INSERT OR IGNORE INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const e of updates.evoluciones) {
stmt.run(e.id, e.internacionId, e.fecha, e.hora || '', e.medico || '', JSON.stringify(e.signosVitales || {}), JSON.stringify(e.examenFisico || {}), e.novedades || '', e.comentarios || '', e.pendientes || '');
}
}
}
})();
res.json({ ok: true });
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message });
}
});
app.listen(PORT, () => {
console.log(`API server listening on http://localhost:${PORT}`);
console.log(`Server running on port ${PORT}`);
});
// === AUTENTICACIÓN ===
@@ -191,19 +240,19 @@ function generateUUID() {
});
}
app.post('/api/auth/login', async (req, res) => {
app.post('/api/auth/login', (req, res) => {
try {
const { dni, password } = req.body;
if (!dni || !password) {
return res.status(400).json({ error: 'DNI y contraseña requeridos' });
}
const usuario = await getUsuarioByDni(dni);
const usuario = getUsuarioByDni(dni);
if (!usuario) {
return res.status(401).json({ error: 'Usuario no encontrado' });
}
const validPassword = await verifyPassword(dni, password);
const validPassword = verifyPassword(dni, password);
if (!validPassword) {
return res.status(401).json({ error: 'Contraseña incorrecta' });
}
@@ -216,20 +265,20 @@ app.post('/api/auth/login', async (req, res) => {
}
});
app.post('/api/auth/change-password', async (req, res) => {
app.post('/api/auth/change-password', (req, res) => {
try {
const { dni, oldPassword, newPassword } = req.body;
if (!dni || !oldPassword || !newPassword) {
return res.status(400).json({ error: 'Todos los campos son requeridos' });
}
const validPassword = await verifyPassword(dni, oldPassword);
const validPassword = verifyPassword(dni, oldPassword);
if (!validPassword) {
return res.status(401).json({ error: 'Contraseña actual incorrecta' });
}
const newHash = await hashPassword(newPassword);
await updateUsuarioByDni(dni, { passwordHash: newHash });
const newHash = hashPassword(newPassword);
updateUsuarioByDni(dni, { passwordHash: newHash });
res.json({ ok: true });
} catch (err) {
console.error(err);
@@ -237,14 +286,14 @@ app.post('/api/auth/change-password', async (req, res) => {
}
});
app.put('/api/auth/update-email', async (req, res) => {
app.put('/api/auth/update-email', (req, res) => {
try {
const { dni, newEmail } = req.body;
if (!dni || !newEmail) {
return res.status(400).json({ error: 'DNI y email son requeridos' });
}
await updateUsuarioByDni(dni, { email: newEmail });
updateUsuarioByDni(dni, { email: newEmail });
res.json({ ok: true });
} catch (err) {
console.error(err);
@@ -254,9 +303,9 @@ app.put('/api/auth/update-email', async (req, res) => {
// === GESTIÓN DE USUARIOS (ADMIN) ===
app.get('/api/areas', async (req, res) => {
app.get('/api/areas', (req, res) => {
try {
const areas = await getAllAreas();
const areas = getAllAreas();
res.json(areas);
} catch (err) {
console.error(err);
@@ -264,9 +313,9 @@ app.get('/api/areas', async (req, res) => {
}
});
app.get('/api/usuarios', async (req, res) => {
app.get('/api/usuarios', (req, res) => {
try {
const usuarios = await getAllUsuarios();
const usuarios = getAllUsuarios();
const usuariosSinPassword = usuarios.map(({ passwordHash, ...u }) => u);
res.json(usuariosSinPassword);
} catch (err) {
@@ -275,7 +324,7 @@ app.get('/api/usuarios', async (req, res) => {
}
});
app.post('/api/usuarios', async (req, res) => {
app.post('/api/usuarios', (req, res) => {
try {
const { apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, password, areaId } = req.body;
@@ -283,12 +332,12 @@ app.post('/api/usuarios', async (req, res) => {
return res.status(400).json({ error: 'Todos los campos obligatorios deben estar presentes' });
}
const existente = await getUsuarioByDni(dni);
const existente = getUsuarioByDni(dni);
if (existente) {
return res.status(400).json({ error: 'Ya existe un usuario con ese DNI' });
}
const passwordHash = await hashPassword(password);
const passwordHash = hashPassword(password);
const usuario = {
id: generateUUID(),
apellido,
@@ -303,7 +352,7 @@ app.post('/api/usuarios', async (req, res) => {
fechaCreacion: new Date().toISOString().split('T')[0]
};
await createUsuario(usuario);
createUsuario(usuario);
res.json({ ...usuario, passwordHash: undefined });
} catch (err) {
console.error(err);
@@ -311,12 +360,12 @@ app.post('/api/usuarios', async (req, res) => {
}
});
app.put('/api/usuarios/:id', async (req, res) => {
app.put('/api/usuarios/:id', (req, res) => {
try {
const { id } = req.params;
const { apellido, nombre, fechaNacimiento, email, rol, matriculaProfesional, areaId, password } = req.body;
const usuario = await getUsuarioById(id);
const usuario = getUsuarioById(id);
if (!usuario) {
return res.status(404).json({ error: 'Usuario no encontrado' });
}
@@ -330,10 +379,10 @@ app.put('/api/usuarios/:id', async (req, res) => {
if (matriculaProfesional !== undefined) datos.matriculaProfesional = matriculaProfesional;
if (areaId !== undefined) datos.areaId = areaId;
if (password) {
datos.passwordHash = await hashPassword(password);
datos.passwordHash = hashPassword(password);
}
await updateUsuario(id, datos);
updateUsuario(id, datos);
res.json({ ok: true });
} catch (err) {
console.error(err);
@@ -341,10 +390,10 @@ app.put('/api/usuarios/:id', async (req, res) => {
}
});
app.delete('/api/usuarios/:id', async (req, res) => {
app.delete('/api/usuarios/:id', (req, res) => {
try {
const { id } = req.params;
await deleteUsuario(id);
deleteUsuario(id);
res.json({ ok: true });
} catch (err) {
console.error(err);
@@ -352,32 +401,23 @@ app.delete('/api/usuarios/:id', async (req, res) => {
}
});
app.put('/api/camas/:id', async (req, res) => {
let retries = 3;
while (retries > 0) {
try {
const { id } = req.params;
const { estado } = req.body;
const db = await openDb();
await db.run('UPDATE camaS SET estado = ? WHERE id = ?', [estado, id]);
await db.close();
return res.json({ ok: true });
} catch (err) {
console.error('Error updating cama (retry', retries, '):', err.message);
retries--;
if (retries === 0) {
return res.status(500).json({ error: err.message });
}
await new Promise(r => setTimeout(r, 200));
}
app.put('/api/camas/:id', (req, res) => {
try {
const { id } = req.params;
const { estado } = req.body;
const db = getDb();
db.prepare('UPDATE camas SET estado = ? WHERE id = ?').run(estado, id);
res.json({ ok: true });
} catch (err) {
console.error('Error updating cama:', err.message);
res.status(500).json({ error: err.message });
}
});
// Helper para actualizar por DNI
async function updateUsuarioByDni(dni, datos) {
const { getUsuarioByDni, updateUsuario } = await import('./db.js');
const usuario = await getUsuarioByDni(dni);
function updateUsuarioByDni(dni, datos) {
const usuario = getUsuarioByDni(dni);
if (usuario) {
await updateUsuario(usuario.id, datos);
updateUsuario(usuario.id, datos);
}
}