Feat: agregar soporte para MariaDB y API routes individuales

- Crear server/db-mariadb.js con conexión a MariaDB usando los datos proporcionados
- Crear server/api-mariadb.js con endpoints individuales para todas las entidades
- Crear server/migrate-to-mariadb.js para migrar datos de SQLite a MariaDB
- Todas las operaciones CRUD disponibles via API REST
This commit is contained in:
2026-04-24 02:35:09 -03:00
parent 8a617265b2
commit f80879779a
3 changed files with 1405 additions and 0 deletions
+657
View File
@@ -0,0 +1,657 @@
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());
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, 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() };
await createMovimientoIndicacion(movimiento);
res.json(movimiento);
} catch (err) {
res.status(500).json({ error: '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);
});