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, getAllAcidosBase, createAcidoBase, deleteAcidoBase, getAllCultivos, createCultivo, updateCultivo, deleteCultivo, getAllEstudiosComplementarios, createEstudioComplementario, deleteEstudioComplementario, getAllInterconsultas, createInterconsulta, updateInterconsulta, deleteInterconsulta, getAllAtb, createAtb, deleteAtb, getAllIndicaciones, createIndicacion, updateIndicacion, deleteIndicacion, getAllMovimientosIndicaciones, createMovimientoIndicacion, getValue, setValue } from './db-mariadb.js'; 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 state = { pacientes: await getAllPacientes(), areas: await getAllAreas(), camas: await getAllCamas(), internaciones: await getAllInternaciones(), 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(), 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: 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 ========== app.get('/api/areas', async (req, res) => { try { res.json(await getAllAreas()); } catch (err) { res.status(500).json({ error: 'Error al obtener areas' }); } }); app.post('/api/areas', async (req, res) => { try { const area = { ...req.body, id: generateUUID() }; await createArea(area); res.json(area); } catch (err) { res.status(500).json({ error: 'Error al crear area' }); } }); app.put('/api/areas/: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 area' }); } }); app.delete('/api/areas/:id', async (req, res) => { try { await deleteArea(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: 'Error al eliminar area' }); } }); // ========== 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: generateUUID(), 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: 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: 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' }); } }); // ========== 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: generateUUID() }; await createAcidoBase(acido); res.json(acido); } catch (err) { res.status(500).json({ error: 'Error al crear 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: 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' }); } }); // ========== 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: generateUUID() }; await createEstudioComplementario(estudio); res.json(estudio); } catch (err) { res.status(500).json({ error: 'Error al crear 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: 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: generateUUID() }; await createAtb(atb); res.json(atb); } catch (err) { res.status(500).json({ error: 'Error al crear 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: 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: 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' }); } }); // ========== 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 = 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 = 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' }); } }); // Initialize DB and start server const PORT = process.env.PORT || 4000; const HOST = process.env.HOST || '0.0.0.0'; initDb().then(() => { app.listen(Number(PORT), HOST, () => { console.log(`MariaDB Server running on http://${HOST}:${PORT}`); }); }).catch(err => { console.error('Failed to initialize database:', err); process.exit(1); });