1111 lines
32 KiB
JavaScript
1111 lines
32 KiB
JavaScript
import express from 'express';
|
|
import cors from 'cors';
|
|
import { initDb,
|
|
getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword,
|
|
getAllPacientes, getPacienteById, createPaciente, updatePaciente, deletePaciente,
|
|
getAllAreas, createArea, updateArea, deleteArea,
|
|
getAllCamas, getCamaById, createCama, updateCama, deleteCama,
|
|
getAllInternaciones, getInternacionById, createInternacion, updateInternacion, deleteInternacion,
|
|
getAllEvoluciones, createEvolucion, updateEvolucion, deleteEvolucion,
|
|
getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio,
|
|
getAllOtrosLaboratorios, createOtroLaboratorio, updateOtroLaboratorio, deleteOtroLaboratorio,
|
|
getAllGlucemias, createGlucemia, updateGlucemia, deleteGlucemia,
|
|
getAllAcidosBase, createAcidoBase, updateAcidoBase, deleteAcidoBase,
|
|
getAllCultivos, createCultivo, updateCultivo, deleteCultivo,
|
|
getAllTiposCultivo, createTipoCultivo, updateTipoCultivo, deleteTipoCultivo, restablecerTiposCultivo,
|
|
getAllGruposLaboratorio, createGrupoLaboratorio, updateGrupoLaboratorio, deleteGrupoLaboratorio, restablecerGruposLaboratorio,
|
|
getAllEstudiosComplementarios, createEstudioComplementario, updateEstudioComplementario, deleteEstudioComplementario,
|
|
getAllInterconsultas, createInterconsulta, updateInterconsulta, deleteInterconsulta,
|
|
getAllAtb, createAtb, updateAtb, deleteAtb,
|
|
getAllIndicaciones, createIndicacion, updateIndicacion, deleteIndicacion,
|
|
getAllMovimientosIndicaciones, createMovimientoIndicacion,
|
|
getAllPendientes, createPendiente, updatePendiente, deletePendiente,
|
|
getValue, setValue,
|
|
getDb, exportAllData, importAllData
|
|
} from './db-mongodb.js';
|
|
|
|
// Capture live logs in-memory
|
|
const logBuffer = [];
|
|
const originalLog = console.log;
|
|
const originalError = console.error;
|
|
const originalWarn = console.warn;
|
|
|
|
function addLog(type, args) {
|
|
const message = args.map(arg => {
|
|
if (typeof arg === 'object') {
|
|
try { return JSON.stringify(arg); } catch { return String(arg); }
|
|
}
|
|
return String(arg);
|
|
}).join(' ');
|
|
const logEntry = `[${new Date().toISOString()}] [${type}] ${message}`;
|
|
logBuffer.push(logEntry);
|
|
if (logBuffer.length > 500) {
|
|
logBuffer.shift();
|
|
}
|
|
}
|
|
|
|
console.log = (...args) => {
|
|
addLog('INFO', args);
|
|
originalLog.apply(console, args);
|
|
};
|
|
console.error = (...args) => {
|
|
addLog('ERROR', args);
|
|
originalError.apply(console, args);
|
|
};
|
|
console.warn = (...args) => {
|
|
addLog('WARN', args);
|
|
originalWarn.apply(console, args);
|
|
};
|
|
|
|
const app = express();
|
|
app.use(cors({
|
|
origin: true,
|
|
credentials: true
|
|
}));
|
|
app.use(express.json({ limit: '50mb' }));
|
|
|
|
// UUID generator
|
|
function generateUUID() {
|
|
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
return crypto.randomUUID();
|
|
}
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
const r = (Math.random() * 16) | 0;
|
|
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
// ========== STATE ENDPOINT (initial load) ==========
|
|
app.get('/api/state', async (req, res) => {
|
|
try {
|
|
const [
|
|
pacientes,
|
|
areasList,
|
|
camas,
|
|
internaciones,
|
|
rawEvoluciones,
|
|
rawLaboratorios,
|
|
otrosLaboratorios,
|
|
glucemias,
|
|
acidosBase,
|
|
cultivos,
|
|
tiposCultivo,
|
|
gruposDeterminacionesLab,
|
|
estudiosComplementarios,
|
|
rawInterconsultas,
|
|
atb,
|
|
indicaciones,
|
|
movimientosIndicaciones,
|
|
pendientes,
|
|
usuariosList
|
|
] = await Promise.all([
|
|
getAllPacientes(),
|
|
getAllAreas(),
|
|
getAllCamas(),
|
|
getAllInternaciones(),
|
|
getAllEvoluciones(),
|
|
getAllLaboratorios(),
|
|
getAllOtrosLaboratorios(),
|
|
getAllGlucemias(),
|
|
getAllAcidosBase(),
|
|
getAllCultivos(),
|
|
getAllTiposCultivo(),
|
|
getAllGruposLaboratorio(),
|
|
getAllEstudiosComplementarios(),
|
|
getAllInterconsultas(),
|
|
getAllAtb(),
|
|
getAllIndicaciones(),
|
|
getAllMovimientosIndicaciones(),
|
|
getAllPendientes(),
|
|
getAllUsuarios()
|
|
]);
|
|
|
|
const evoluciones = rawEvoluciones.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
|
|
}));
|
|
|
|
const laboratorios = rawLaboratorios.map(l => ({
|
|
...l,
|
|
resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados
|
|
}));
|
|
|
|
const interconsultas = rawInterconsultas.map(ic => ({
|
|
...ic,
|
|
realizada: !!ic.realizada
|
|
}));
|
|
|
|
const usuarios = usuariosList.map(({ passwordHash, ...u }) => u);
|
|
|
|
const state = {
|
|
pacientes,
|
|
areas: areasList,
|
|
grupos: areasList,
|
|
camas,
|
|
internaciones,
|
|
evoluciones,
|
|
laboratorios,
|
|
otrosLaboratorios,
|
|
glucemias,
|
|
acidosBase,
|
|
cultivos,
|
|
tiposCultivo,
|
|
gruposDeterminacionesLab,
|
|
estudiosComplementarios,
|
|
interconsultas,
|
|
atb,
|
|
indicaciones,
|
|
movimientosIndicaciones,
|
|
pendientes,
|
|
usuarios,
|
|
vistaActual: 'dashboard',
|
|
currentInternacionId: null
|
|
};
|
|
res.json(state);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: 'failed to read state' });
|
|
}
|
|
});
|
|
|
|
// ========== USUARIOS ==========
|
|
app.get('/api/usuarios', async (req, res) => {
|
|
try {
|
|
const usuarios = await getAllUsuarios();
|
|
const usuariosSinPassword = usuarios.map(({ passwordHash, ...u }) => u);
|
|
res.json(usuariosSinPassword);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener usuarios' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/usuarios', async (req, res) => {
|
|
try {
|
|
const { apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, password, areaId } = req.body;
|
|
|
|
if (!apellido || !nombre || !dni || !fechaNacimiento || !email || !rol || !password) {
|
|
return res.status(400).json({ error: 'Todos los campos obligatorios deben estar presentes' });
|
|
}
|
|
|
|
const existente = await getUsuarioByDni(dni);
|
|
if (existente) {
|
|
return res.status(400).json({ error: 'Ya existe un usuario con ese DNI' });
|
|
}
|
|
|
|
const passwordHash = hashPassword(password);
|
|
const usuario = {
|
|
id: generateUUID(),
|
|
apellido, nombre, dni, fechaNacimiento, email, rol,
|
|
matriculaProfesional: matriculaProfesional || null,
|
|
passwordHash,
|
|
areaId: areaId || null,
|
|
fechaCreacion: new Date().toISOString().split('T')[0]
|
|
};
|
|
|
|
await createUsuario(usuario);
|
|
res.json({ ...usuario, passwordHash: undefined });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear usuario' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/usuarios/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { apellido, nombre, fechaNacimiento, email, rol, matriculaProfesional, areaId, password } = req.body;
|
|
|
|
const usuario = await getUsuarioById(id);
|
|
if (!usuario) {
|
|
return res.status(404).json({ error: 'Usuario no encontrado' });
|
|
}
|
|
|
|
const datos = {};
|
|
if (apellido !== undefined) datos.apellido = apellido;
|
|
if (nombre !== undefined) datos.nombre = nombre;
|
|
if (fechaNacimiento !== undefined) datos.fechaNacimiento = fechaNacimiento;
|
|
if (email !== undefined) datos.email = email;
|
|
if (rol !== undefined) datos.rol = rol;
|
|
if (matriculaProfesional !== undefined) datos.matriculaProfesional = matriculaProfesional;
|
|
if (areaId !== undefined) datos.areaId = areaId;
|
|
if (password) datos.passwordHash = hashPassword(password);
|
|
|
|
await updateUsuario(id, datos);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar usuario' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/usuarios/:id', async (req, res) => {
|
|
try {
|
|
await deleteUsuario(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar usuario' });
|
|
}
|
|
});
|
|
|
|
// ========== PACIENTES ==========
|
|
app.get('/api/pacientes', async (req, res) => {
|
|
try {
|
|
res.json(await getAllPacientes());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener pacientes' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/pacientes', async (req, res) => {
|
|
try {
|
|
const paciente = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createPaciente(paciente);
|
|
res.json(paciente);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear paciente' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/pacientes/:id', async (req, res) => {
|
|
try {
|
|
await updatePaciente(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar paciente' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/pacientes/:id', async (req, res) => {
|
|
try {
|
|
await deletePaciente(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar paciente' });
|
|
}
|
|
});
|
|
|
|
// ========== AREAS & GRUPOS ==========
|
|
app.get(['/api/areas', '/api/grupos'], async (req, res) => {
|
|
try {
|
|
res.json(await getAllAreas());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener áreas/grupos' });
|
|
}
|
|
});
|
|
|
|
app.post(['/api/areas', '/api/grupos'], async (req, res) => {
|
|
try {
|
|
const area = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createArea(area);
|
|
res.json(area);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear área/grupo' });
|
|
}
|
|
});
|
|
|
|
app.put(['/api/areas/:id', '/api/grupos/:id'], async (req, res) => {
|
|
try {
|
|
await updateArea(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar área/grupo' });
|
|
}
|
|
});
|
|
|
|
app.delete(['/api/areas/:id', '/api/grupos/:id'], async (req, res) => {
|
|
try {
|
|
await deleteArea(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar área/grupo' });
|
|
}
|
|
});
|
|
|
|
// ========== CAMAS ==========
|
|
app.get('/api/camas', async (req, res) => {
|
|
try {
|
|
res.json(await getAllCamas());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener camas' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/camas/:id', async (req, res) => {
|
|
try {
|
|
await updateCama(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar cama' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/camas', async (req, res) => {
|
|
try {
|
|
const cama = req.body;
|
|
if (!cama.id) {
|
|
cama.id = generateUUID();
|
|
}
|
|
await createCama(cama);
|
|
res.json(cama);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear cama' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/camas/:id', async (req, res) => {
|
|
try {
|
|
await deleteCama(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar cama' });
|
|
}
|
|
});
|
|
|
|
// ========== INTERNACIONES ==========
|
|
app.get('/api/internaciones', async (req, res) => {
|
|
try {
|
|
res.json(await getAllInternaciones());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener internaciones' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/internaciones', async (req, res) => {
|
|
try {
|
|
const internacion = { ...req.body, id: req.body.id || generateUUID(), activa: req.body.activa !== undefined ? req.body.activa : true };
|
|
await createInternacion(internacion);
|
|
res.json(internacion);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear internacion' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/internaciones/:id', async (req, res) => {
|
|
try {
|
|
await updateInternacion(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar internacion' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/internaciones/:id', async (req, res) => {
|
|
try {
|
|
await deleteInternacion(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar internacion' });
|
|
}
|
|
});
|
|
|
|
// ========== EVOLUCIONES ==========
|
|
app.get('/api/evoluciones', async (req, res) => {
|
|
try {
|
|
res.json(await getAllEvoluciones());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener evoluciones' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/evoluciones', async (req, res) => {
|
|
try {
|
|
const evolucion = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createEvolucion(evolucion);
|
|
res.json(evolucion);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear evolucion' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/evoluciones/:id', async (req, res) => {
|
|
try {
|
|
await updateEvolucion(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar evolucion' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/evoluciones/:id', async (req, res) => {
|
|
try {
|
|
await deleteEvolucion(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar evolucion' });
|
|
}
|
|
});
|
|
|
|
// ========== LABORATORIOS ==========
|
|
app.get('/api/laboratorios', async (req, res) => {
|
|
try {
|
|
res.json(await getAllLaboratorios());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener laboratorios' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/laboratorios', async (req, res) => {
|
|
try {
|
|
const laboratorio = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createLaboratorio(laboratorio);
|
|
res.json(laboratorio);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear laboratorio' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/laboratorios/:id', async (req, res) => {
|
|
try {
|
|
await updateLaboratorio(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar laboratorio' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/laboratorios/:id', async (req, res) => {
|
|
try {
|
|
await deleteLaboratorio(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar laboratorio' });
|
|
}
|
|
});
|
|
|
|
|
|
// ========== OTROS LABORATORIOS ==========
|
|
app.get('/api/otros-laboratorios', async (req, res) => {
|
|
try {
|
|
const records = await getAllOtrosLaboratorios();
|
|
res.json(records);
|
|
} catch (error) {
|
|
console.error('Error fetching otros-laboratorios:', error);
|
|
res.status(500).json({ error: 'Failed to fetch otros-laboratorios' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/otros-laboratorios', async (req, res) => {
|
|
try {
|
|
const { pacienteId, fecha } = req.body;
|
|
if (!pacienteId || !fecha) {
|
|
return res.status(400).json({ error: 'pacienteId and fecha are required' });
|
|
}
|
|
|
|
const record = {
|
|
id: generateUUID(),
|
|
observaciones: '',
|
|
...req.body
|
|
};
|
|
|
|
const newRecord = await createOtroLaboratorio(record);
|
|
res.status(201).json(newRecord);
|
|
} catch (error) {
|
|
console.error('Error creating otro laboratorio:', error);
|
|
res.status(500).json({ error: 'Failed to create otro laboratorio' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/otros-laboratorios/:id', async (req, res) => {
|
|
try {
|
|
const result = await updateOtroLaboratorio(req.params.id, req.body);
|
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
|
res.json(result);
|
|
} catch (error) {
|
|
console.error('Error updating otro laboratorio:', error);
|
|
res.status(500).json({ error: 'Failed to update otro laboratorio' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/otros-laboratorios/:id', async (req, res) => {
|
|
try {
|
|
const result = await deleteOtroLaboratorio(req.params.id);
|
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
|
res.json({ message: 'Deleted successfully' });
|
|
} catch (error) {
|
|
console.error('Error deleting otro laboratorio:', error);
|
|
res.status(500).json({ error: 'Failed to delete otro laboratorio' });
|
|
}
|
|
});
|
|
|
|
// ========== GLUCEMIAS ==========
|
|
app.get('/api/glucemias', async (req, res) => {
|
|
try {
|
|
res.json(await getAllGlucemias());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener glucemias' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/glucemias', async (req, res) => {
|
|
try {
|
|
const glucemia = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createGlucemia(glucemia);
|
|
res.json(glucemia);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear glucemia' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/glucemias/:id', async (req, res) => {
|
|
try {
|
|
await updateGlucemia(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar glucemia' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/glucemias/:id', async (req, res) => {
|
|
try {
|
|
await deleteGlucemia(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar glucemia' });
|
|
}
|
|
});
|
|
|
|
// ========== ACIDOS BASE ==========
|
|
app.get('/api/acid-os-base', async (req, res) => {
|
|
try {
|
|
res.json(await getAllAcidosBase());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener acidos base' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/acid-os-base', async (req, res) => {
|
|
try {
|
|
const acido = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createAcidoBase(acido);
|
|
res.json(acido);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear acido base' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/acid-os-base/:id', async (req, res) => {
|
|
try {
|
|
await updateAcidoBase(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar acido base' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/acid-os-base/:id', async (req, res) => {
|
|
try {
|
|
await deleteAcidoBase(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar acido base' });
|
|
}
|
|
});
|
|
|
|
// ========== CULTIVOS ==========
|
|
app.get('/api/cultivos', async (req, res) => {
|
|
try {
|
|
res.json(await getAllCultivos());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener cultivos' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/cultivos', async (req, res) => {
|
|
try {
|
|
const cultivo = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createCultivo(cultivo);
|
|
res.json(cultivo);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear cultivo' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/cultivos/:id', async (req, res) => {
|
|
try {
|
|
await updateCultivo(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar cultivo' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/cultivos/:id', async (req, res) => {
|
|
try {
|
|
await deleteCultivo(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar cultivo' });
|
|
}
|
|
});
|
|
|
|
// ========== TIPOS DE CULTIVO ==========
|
|
app.get('/api/tipos-cultivo', async (req, res) => {
|
|
try {
|
|
res.json(await getAllTiposCultivo());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener tipos de cultivo' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/tipos-cultivo', async (req, res) => {
|
|
try {
|
|
const nuevo = await createTipoCultivo(req.body);
|
|
res.json(nuevo);
|
|
} catch (err) {
|
|
res.status(400).json({ error: err.message || 'Error al crear tipo de cultivo' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/tipos-cultivo/:id', async (req, res) => {
|
|
try {
|
|
const updated = await updateTipoCultivo(req.params.id, req.body);
|
|
res.json(updated);
|
|
} catch (err) {
|
|
res.status(400).json({ error: err.message || 'Error al actualizar tipo de cultivo' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/tipos-cultivo/:id', async (req, res) => {
|
|
try {
|
|
await deleteTipoCultivo(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar tipo de cultivo' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/tipos-cultivo/reset', async (req, res) => {
|
|
try {
|
|
const list = await restablecerTiposCultivo();
|
|
res.json(list);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al restablecer tipos de cultivo' });
|
|
}
|
|
});
|
|
|
|
// ========== GRUPOS DE DETERMINACIONES DE LABORATORIO ==========
|
|
app.get('/api/grupos-laboratorio', async (req, res) => {
|
|
try {
|
|
res.json(await getAllGruposLaboratorio());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener grupos de determinaciones de laboratorio' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/grupos-laboratorio', async (req, res) => {
|
|
try {
|
|
const nuevo = await createGrupoLaboratorio(req.body);
|
|
res.json(nuevo);
|
|
} catch (err) {
|
|
res.status(400).json({ error: err.message || 'Error al crear grupo de determinaciones' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/grupos-laboratorio/:id', async (req, res) => {
|
|
try {
|
|
const updated = await updateGrupoLaboratorio(req.params.id, req.body);
|
|
res.json(updated);
|
|
} catch (err) {
|
|
res.status(400).json({ error: err.message || 'Error al actualizar grupo de determinaciones' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/grupos-laboratorio/:id', async (req, res) => {
|
|
try {
|
|
await deleteGrupoLaboratorio(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar grupo de determinaciones' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/grupos-laboratorio/reset', async (req, res) => {
|
|
try {
|
|
const list = await restablecerGruposLaboratorio();
|
|
res.json(list);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al restablecer grupos de determinaciones' });
|
|
}
|
|
});
|
|
|
|
// ========== ESTUDIOS COMPLEMENTARIOS ==========
|
|
app.get('/api/estudios-complementarios', async (req, res) => {
|
|
try {
|
|
res.json(await getAllEstudiosComplementarios());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener estudios' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/estudios-complementarios', async (req, res) => {
|
|
try {
|
|
const estudio = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createEstudioComplementario(estudio);
|
|
res.json(estudio);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear estudio' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/estudios-complementarios/:id', async (req, res) => {
|
|
try {
|
|
await updateEstudioComplementario(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar estudio' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/estudios-complementarios/:id', async (req, res) => {
|
|
try {
|
|
await deleteEstudioComplementario(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar estudio' });
|
|
}
|
|
});
|
|
|
|
// ========== INTERCONSULTAS ==========
|
|
app.get('/api/interconsultas', async (req, res) => {
|
|
try {
|
|
res.json(await getAllInterconsultas());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener interconsultas' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/interconsultas', async (req, res) => {
|
|
try {
|
|
const interconsulta = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createInterconsulta(interconsulta);
|
|
res.json(interconsulta);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear interconsulta' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/interconsultas/:id', async (req, res) => {
|
|
try {
|
|
await updateInterconsulta(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar interconsulta' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/interconsultas/:id', async (req, res) => {
|
|
try {
|
|
await deleteInterconsulta(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar interconsulta' });
|
|
}
|
|
});
|
|
|
|
// ========== ATB ==========
|
|
app.get('/api/atb', async (req, res) => {
|
|
try {
|
|
res.json(await getAllAtb());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener ATB' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/atb', async (req, res) => {
|
|
try {
|
|
const atb = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createAtb(atb);
|
|
res.json(atb);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear ATB' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/atb/:id', async (req, res) => {
|
|
try {
|
|
await updateAtb(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar ATB' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/atb/:id', async (req, res) => {
|
|
try {
|
|
await deleteAtb(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar ATB' });
|
|
}
|
|
});
|
|
|
|
// ========== INDICACIONES ==========
|
|
app.get('/api/indicaciones', async (req, res) => {
|
|
try {
|
|
res.json(await getAllIndicaciones());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener indicaciones' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/indicaciones', async (req, res) => {
|
|
try {
|
|
const indicacion = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createIndicacion(indicacion);
|
|
res.json(indicacion);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear indicacion' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/indicaciones/:id', async (req, res) => {
|
|
try {
|
|
await updateIndicacion(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar indicacion' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/indicaciones/:id', async (req, res) => {
|
|
try {
|
|
await deleteIndicacion(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar indicacion' });
|
|
}
|
|
});
|
|
|
|
// ========== MOVIMIENTOS INDICACIONES ==========
|
|
app.get('/api/movimientos-indicaciones', async (req, res) => {
|
|
try {
|
|
res.json(await getAllMovimientosIndicaciones());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener movimientos' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/movimientos-indicaciones', async (req, res) => {
|
|
try {
|
|
const movimiento = { ...req.body, id: req.body.id || generateUUID() };
|
|
console.log('Creating movimiento:', movimiento);
|
|
await createMovimientoIndicacion(movimiento);
|
|
res.json(movimiento);
|
|
} catch (err) {
|
|
console.error('Error creating movimiento:', err);
|
|
res.status(500).json({ error: err.message || 'Error al crear movimiento' });
|
|
}
|
|
});
|
|
|
|
// ========== PENDIENTES ==========
|
|
app.get('/api/pendientes', async (req, res) => {
|
|
try {
|
|
res.json(await getAllPendientes());
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al obtener pendientes' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/pendientes', async (req, res) => {
|
|
try {
|
|
const pendiente = { ...req.body, id: req.body.id || generateUUID() };
|
|
await createPendiente(pendiente);
|
|
res.json(pendiente);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al crear pendiente' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/pendientes/:id', async (req, res) => {
|
|
try {
|
|
await updatePendiente(req.params.id, req.body);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar pendiente' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/pendientes/:id', async (req, res) => {
|
|
try {
|
|
await deletePendiente(req.params.id);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al eliminar pendiente' });
|
|
}
|
|
});
|
|
|
|
// ========== AUTH ==========
|
|
app.post('/api/auth/login', async (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);
|
|
if (!usuario) {
|
|
return res.status(401).json({ error: 'Usuario no encontrado' });
|
|
}
|
|
|
|
const validPassword = await verifyPassword(dni, password);
|
|
if (!validPassword) {
|
|
return res.status(401).json({ error: 'Contraseña incorrecta' });
|
|
}
|
|
|
|
const { passwordHash, ...userWithoutPassword } = usuario;
|
|
res.json(userWithoutPassword);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error en autenticación' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/auth/change-password', async (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);
|
|
if (!validPassword) {
|
|
return res.status(401).json({ error: 'Contraseña actual incorrecta' });
|
|
}
|
|
|
|
const newHash = hashPassword(newPassword);
|
|
const usuario = await getUsuarioByDni(dni);
|
|
if (usuario) {
|
|
await updateUsuario(usuario.id, { passwordHash: newHash });
|
|
}
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al cambiar contraseña' });
|
|
}
|
|
});
|
|
|
|
app.put('/api/auth/update-email', async (req, res) => {
|
|
try {
|
|
const { dni, newEmail } = req.body;
|
|
if (!dni || !newEmail) {
|
|
return res.status(400).json({ error: 'DNI y email son requeridos' });
|
|
}
|
|
|
|
const usuario = await getUsuarioByDni(dni);
|
|
if (usuario) {
|
|
await updateUsuario(usuario.id, { email: newEmail });
|
|
}
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al actualizar email' });
|
|
}
|
|
});
|
|
|
|
// ========== ADMIN SYSTEM ENDPOINTS ==========
|
|
|
|
app.post('/api/admin/mongosh', async (req, res) => {
|
|
try {
|
|
const { command } = req.body;
|
|
if (!command) {
|
|
return res.status(400).json({ error: 'Comando requerido' });
|
|
}
|
|
|
|
const dbInstance = getDb();
|
|
if (!dbInstance) {
|
|
return res.json({ result: 'Advertencia: MongoDB no está conectado. Utilizando store en memoria. Las consultas de mongosh no están disponibles.', isMemStore: true });
|
|
}
|
|
|
|
const dbProxy = new Proxy(dbInstance, {
|
|
get(target, prop) {
|
|
if (typeof target[prop] !== 'undefined') {
|
|
if (typeof target[prop] === 'function') {
|
|
return target[prop].bind(target);
|
|
}
|
|
return target[prop];
|
|
}
|
|
return target.collection(prop);
|
|
}
|
|
});
|
|
|
|
const { ObjectId } = await import('mongodb');
|
|
|
|
const evalFn = new Function('db', 'ObjectId', `
|
|
return (async () => {
|
|
${command.trim().includes('return') || command.trim().startsWith('{') || command.trim().includes(';') ? command : 'return (' + command + ')'}
|
|
})();
|
|
`);
|
|
|
|
let finalResult = await evalFn(dbProxy, ObjectId);
|
|
|
|
// Si el resultado es un cursor de MongoDB (ej. db.collection.find()), lo convertimos a array
|
|
if (finalResult && typeof finalResult === 'object' && typeof finalResult.toArray === 'function') {
|
|
finalResult = await finalResult.toArray();
|
|
}
|
|
|
|
res.json({ result: finalResult });
|
|
} catch (err) {
|
|
res.status(400).json({ error: err.message || String(err) });
|
|
}
|
|
});
|
|
|
|
app.get('/api/admin/db/export', async (req, res) => {
|
|
try {
|
|
const dump = await exportAllData();
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.setHeader('Content-Disposition', 'attachment; filename=hospital_backup.json');
|
|
res.json(dump);
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al exportar base de datos: ' + err.message });
|
|
}
|
|
});
|
|
|
|
app.post('/api/admin/db/import', async (req, res) => {
|
|
try {
|
|
const dump = req.body;
|
|
if (!dump || typeof dump !== 'object') {
|
|
return res.status(400).json({ error: 'Formato de importación inválido' });
|
|
}
|
|
await importAllData(dump);
|
|
res.json({ ok: true });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Error al importar base de datos: ' + err.message });
|
|
}
|
|
});
|
|
|
|
app.get('/api/admin/logs', async (req, res) => {
|
|
try {
|
|
let fileLogs = '';
|
|
try {
|
|
const fs = await import('fs/promises');
|
|
fileLogs = await fs.readFile('server_log.txt', 'utf8');
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
const memoryLogs = logBuffer.join('\n');
|
|
const combined = [fileLogs, '\n--- LOGS DE LA SESION EN VIVO ---', memoryLogs].filter(Boolean).join('\n');
|
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
res.send(combined);
|
|
} catch (err) {
|
|
res.status(500).send('Error al leer logs: ' + err.message);
|
|
}
|
|
});
|
|
|
|
// Initialize DB and start server
|
|
const PORT = process.env.PORT || 4000;
|
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
initDb()
|
|
.then(() => {
|
|
app.listen(PORT, HOST, () => {
|
|
console.log(`Backend API running on http://${HOST}:${PORT}`);
|
|
});
|
|
})
|
|
.catch((err) => {
|
|
console.error('Failed to initialize MongoDB in standalone mode:', err);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
export { initDb };
|
|
export default app;
|