Add MongoDB integration and configure Docker container to publish app on port 8084
This commit is contained in:
+34
-16
@@ -11,15 +11,34 @@ RUN npm ci
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN VITE_API_URL=$VITE_API_URL npm run build
|
RUN VITE_API_URL=$VITE_API_URL npm run build
|
||||||
|
|
||||||
# Stage 2: App only (no MariaDB)
|
# Stage 2: Production environment with Node.js, Nginx, and MongoDB Community Edition
|
||||||
FROM node:20-alpine
|
FROM node:20-bookworm-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install nginx and curl
|
# Prevent interactive prompts during apt install
|
||||||
RUN apk add --no-cache nginx curl bash
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
# Copy built frontend to nginx
|
# Install dependencies needed for MongoDB repo and general setup
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gnupg \
|
||||||
|
curl \
|
||||||
|
nginx \
|
||||||
|
procps \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Add MongoDB official GPG key and repository (MongoDB 8.0)
|
||||||
|
RUN curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
|
||||||
|
gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg \
|
||||||
|
&& echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] http://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" | \
|
||||||
|
tee /etc/apt/sources.list.d/mongodb-org-8.0.list
|
||||||
|
|
||||||
|
# Install MongoDB Community Edition
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
mongodb-org \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy built frontend to nginx html folder
|
||||||
COPY --from=builder /build/dist /usr/share/nginx/html
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
|
|
||||||
# Copy server files
|
# Copy server files
|
||||||
@@ -28,24 +47,23 @@ RUN cd ./server && npm ci
|
|||||||
|
|
||||||
COPY server/ ./server/
|
COPY server/ ./server/
|
||||||
|
|
||||||
# Copy nginx config
|
# Configure Nginx for Debian
|
||||||
COPY nginx.conf /etc/nginx/http.d/default.conf
|
RUN rm -f /etc/nginx/sites-enabled/default
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
# Create directories
|
# Create MongoDB data and run directories with correct permissions
|
||||||
RUN mkdir -p /run/nginx
|
RUN mkdir -p /data/db /var/log/mongodb /var/run/mongodb && \
|
||||||
|
chown -R mongodb:mongodb /data/db /var/log/mongodb /var/run/mongodb
|
||||||
|
|
||||||
# Copy entrypoint
|
# Copy entrypoint
|
||||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
RUN chmod +x /docker-entrypoint.sh
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 80 4000
|
# ONLY expose port 80 (HTTP)
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
ENV DB_HOST=db
|
|
||||||
ENV DB_PORT=3306
|
|
||||||
ENV DB_USER=hospital
|
|
||||||
ENV DB_PASSWORD=hospital123
|
|
||||||
ENV DB_NAME=hospital
|
ENV DB_NAME=hospital
|
||||||
ENV ADMIN_DNI=00000000
|
ENV ADMIN_DNI=35242633
|
||||||
ENV ADMIN_PASSWORD=Admin123!
|
ENV ADMIN_PASSWORD=admin123
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
+2
-35
@@ -1,46 +1,13 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
|
||||||
image: mariadb:10.11
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: 1123581321Rt%
|
|
||||||
MYSQL_DATABASE: hospitalsantojanni
|
|
||||||
MYSQL_USER: hospitaluser
|
|
||||||
MYSQL_PASSWORD: 1123581321Rt%
|
|
||||||
ports:
|
|
||||||
- "3307:3306"
|
|
||||||
volumes:
|
|
||||||
- mariadb-data:/var/lib/mysql
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
app:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
args:
|
args:
|
||||||
- VITE_API_URL=/api
|
- VITE_API_URL=/api
|
||||||
ports:
|
ports:
|
||||||
- "8077:80"
|
- "8084:80"
|
||||||
- "4000:4000"
|
|
||||||
environment:
|
environment:
|
||||||
DB_HOST: localhost
|
DB_NAME: hospital
|
||||||
DB_PORT: "3307"
|
|
||||||
DB_USER: hospitaluser
|
|
||||||
DB_PASSWORD: 1123581321Rt%
|
|
||||||
DB_NAME: hospitalsantojanni
|
|
||||||
ADMIN_DNI: "35242633"
|
ADMIN_DNI: "35242633"
|
||||||
ADMIN_PASSWORD: admin123
|
ADMIN_PASSWORD: admin123
|
||||||
volumes:
|
|
||||||
- nginx-data:/usr/share/nginx/html
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
|
||||||
mariadb-data:
|
|
||||||
nginx-data:
|
|
||||||
|
|||||||
+12
-27
@@ -1,34 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
DB_NAME="${DB_NAME:-hospital}"
|
echo "Starting local MongoDB server (only accessible from inside container)..."
|
||||||
DB_USER="${DB_USER:-hospital}"
|
# Start mongod as the default mongodb user, bound to localhost (127.0.0.1) for internal access only
|
||||||
DB_PASSWORD="${DB_PASSWORD:-hospital123}"
|
su -s /bin/bash -c "mongod --dbpath /data/db --bind_ip 127.0.0.1 --fork --logpath /var/log/mongodb/mongod.log" mongodb
|
||||||
|
|
||||||
echo "Waiting for MariaDB to be ready..."
|
echo "Waiting for MongoDB to be ready..."
|
||||||
for i in $(seq 1 60); do
|
for i in $(seq 1 30); do
|
||||||
mysql -h "${DB_HOST:-db}" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -e "SELECT 1" 2>/dev/null && break || sleep 2
|
mongosh --eval "db.adminCommand('ping')" --quiet &>/dev/null && break
|
||||||
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
ADMIN_DNI="${ADMIN_DNI:-00000000}"
|
echo "Starting Nginx reverse proxy..."
|
||||||
ADMIN_PASSWORD="${ADMIN_PASSWORD:-Admin123!}"
|
nginx
|
||||||
|
|
||||||
echo "Starting nginx..."
|
echo "Starting Node.js backend connected to local MongoDB..."
|
||||||
nginx &
|
# Start the Node.js API server using MongoDB
|
||||||
|
exec node server/api-mongodb.js
|
||||||
echo "Waiting for API server..."
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
echo "Creating admin user via API..."
|
|
||||||
curl -s -X POST "http://localhost:4000/api/usuarios" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"apellido\":\"Administrador\",\"nombre\":\"Sistema\",\"dni\":\"$ADMIN_DNI\",\"fechaNacimiento\":\"2024-01-01\",\"email\":\"admin@hospital.local\",\"rol\":\"admin\",\"password\":\"$ADMIN_PASSWORD\"}" \
|
|
||||||
|| echo "Admin user may already exist"
|
|
||||||
|
|
||||||
echo "=========================================="
|
|
||||||
echo "ADMIN CREDENTIALS:"
|
|
||||||
echo "DNI: $ADMIN_DNI"
|
|
||||||
echo "Password: $ADMIN_PASSWORD"
|
|
||||||
echo "=========================================="
|
|
||||||
|
|
||||||
exec node server/api-mariadb.js
|
|
||||||
|
|||||||
@@ -0,0 +1,665 @@
|
|||||||
|
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-mongodb.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(`MongoDB Server running on http://${HOST}:${PORT}`);
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to initialize database:', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,582 @@
|
|||||||
|
import { MongoClient } from 'mongodb';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
const { compareSync, hashSync } = bcrypt;
|
||||||
|
|
||||||
|
export 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017';
|
||||||
|
const DB_NAME = process.env.DB_NAME || 'hospital';
|
||||||
|
|
||||||
|
let client;
|
||||||
|
let db;
|
||||||
|
|
||||||
|
export async function initDb() {
|
||||||
|
try {
|
||||||
|
client = new MongoClient(MONGODB_URI, {
|
||||||
|
connectTimeoutMS: 10000,
|
||||||
|
});
|
||||||
|
await client.connect();
|
||||||
|
db = client.db(DB_NAME);
|
||||||
|
console.log(`Connected to MongoDB database: ${DB_NAME}`);
|
||||||
|
|
||||||
|
// Create unique index for users and patients DNI
|
||||||
|
await db.collection('usuarios').createIndex({ dni: 1 }, { unique: true });
|
||||||
|
await db.collection('pacientes').createIndex({ dni: 1 }, { unique: true });
|
||||||
|
await db.collection('areas').createIndex({ nombre: 1 }, { unique: true });
|
||||||
|
|
||||||
|
// Check if default admin exists
|
||||||
|
const adminCount = await db.collection('usuarios').countDocuments();
|
||||||
|
if (adminCount === 0) {
|
||||||
|
const adminDni = process.env.ADMIN_DNI || '12345678';
|
||||||
|
const adminPassword = process.env.ADMIN_PASSWORD || 'admin123';
|
||||||
|
const adminHash = hashSync(adminPassword, 10);
|
||||||
|
|
||||||
|
const adminUser = {
|
||||||
|
id: 'admin-default',
|
||||||
|
apellido: 'Administrador',
|
||||||
|
nombre: 'Sistema',
|
||||||
|
dni: adminDni,
|
||||||
|
fechaNacimiento: '1990-01-01',
|
||||||
|
email: 'admin@hospital.local',
|
||||||
|
rol: 'admin',
|
||||||
|
passwordHash: adminHash,
|
||||||
|
fechaCreacion: new Date().toISOString().split('T')[0]
|
||||||
|
};
|
||||||
|
await db.collection('usuarios').insertOne(adminUser);
|
||||||
|
console.log(`Default admin created: DNI ${adminDni} / Password ${adminPassword}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize MongoDB:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to remove MongoDB's internal _id from returned objects to keep schema clean
|
||||||
|
function cleanDoc(doc) {
|
||||||
|
if (!doc) return null;
|
||||||
|
const { _id, ...rest } = doc;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanDocs(docs) {
|
||||||
|
return docs.map(cleanDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== USUARIOS ==========
|
||||||
|
export async function getUsuarioByDni(dni) {
|
||||||
|
const user = await db.collection('usuarios').findOne({ dni });
|
||||||
|
return cleanDoc(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUsuarioById(id) {
|
||||||
|
const user = await db.collection('usuarios').findOne({ id });
|
||||||
|
return cleanDoc(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllUsuarios() {
|
||||||
|
const users = await db.collection('usuarios').find().sort({ apellido: 1, nombre: 1 }).toArray();
|
||||||
|
return cleanDocs(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createUsuario(usuario) {
|
||||||
|
await db.collection('usuarios').insertOne(usuario);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUsuario(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('usuarios').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUsuario(id) {
|
||||||
|
await db.collection('usuarios').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyPassword(dni, password) {
|
||||||
|
const user = await db.collection('usuarios').findOne({ dni });
|
||||||
|
if (!user) return false;
|
||||||
|
return compareSync(password, user.passwordHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hashPassword(password) {
|
||||||
|
return hashSync(password, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== PACIENTES ==========
|
||||||
|
export async function getAllPacientes() {
|
||||||
|
const pacientes = await db.collection('pacientes').find().toArray();
|
||||||
|
return cleanDocs(pacientes);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPacienteById(id) {
|
||||||
|
const paciente = await db.collection('pacientes').findOne({ id });
|
||||||
|
return cleanDoc(paciente);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPaciente(paciente) {
|
||||||
|
await db.collection('pacientes').insertOne(paciente);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePaciente(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (key !== 'id' && val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('pacientes').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePaciente(id) {
|
||||||
|
await db.collection('pacientes').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== AREAS ==========
|
||||||
|
export async function getAllAreas() {
|
||||||
|
const areas = await db.collection('areas').find().toArray();
|
||||||
|
return cleanDocs(areas);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createArea(area) {
|
||||||
|
await db.collection('areas').insertOne(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateArea(id, datos) {
|
||||||
|
if (datos.nombre !== undefined) {
|
||||||
|
await db.collection('areas').updateOne({ id }, { $set: { nombre: datos.nombre } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteArea(id) {
|
||||||
|
await db.collection('areas').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== CAMAS ==========
|
||||||
|
export async function getAllCamas() {
|
||||||
|
const camas = await db.collection('camas').find().toArray();
|
||||||
|
return cleanDocs(camas);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCamaById(id) {
|
||||||
|
const cama = await db.collection('camas').findOne({ id });
|
||||||
|
return cleanDoc(cama);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createCama(cama) {
|
||||||
|
const id = cama.id || generateUUID();
|
||||||
|
const doc = {
|
||||||
|
id,
|
||||||
|
numero: cama.numero,
|
||||||
|
areaId: cama.areaId,
|
||||||
|
tipo: cama.tipo || 'Estándar',
|
||||||
|
estado: cama.estado || 'Disponible',
|
||||||
|
pacienteId: cama.pacienteId || null,
|
||||||
|
internacionId: cama.internacionId || null
|
||||||
|
};
|
||||||
|
await db.collection('camas').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCama(id, updates) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(updates)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('camas').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCama(id) {
|
||||||
|
await db.collection('camas').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== INTERNACIONES ==========
|
||||||
|
export async function getAllInternaciones() {
|
||||||
|
const internaciones = await db.collection('internaciones').find().toArray();
|
||||||
|
return cleanDocs(internaciones).map(i => ({ ...i, activa: !!i.activa }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getInternacionById(id) {
|
||||||
|
const internacion = await db.collection('internaciones').findOne({ id });
|
||||||
|
if (internacion) internacion.activa = !!internacion.activa;
|
||||||
|
return cleanDoc(internacion);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createInternacion(internacion) {
|
||||||
|
const doc = {
|
||||||
|
id: internacion.id,
|
||||||
|
pacienteId: internacion.pacienteId,
|
||||||
|
camaId: internacion.camaId || null,
|
||||||
|
areaId: internacion.areaId || null,
|
||||||
|
fechaIngresoHospital: internacion.fechaIngresoHospital || null,
|
||||||
|
fechaIngresoClinica: internacion.fechaIngresoClinica || null,
|
||||||
|
fechaEgreso: internacion.fechaEgreso || null,
|
||||||
|
diagnosticoIngreso: internacion.diagnosticoIngreso || null,
|
||||||
|
motivoConsulta: internacion.motivoConsulta || null,
|
||||||
|
enfermedadActual: internacion.enfermedadActual || null,
|
||||||
|
antecedentesEnfermedadActual: internacion.antecedentesEnfermedadActual || null,
|
||||||
|
diagnosticoEgreso: internacion.diagnosticoEgreso || null,
|
||||||
|
medicoIngresante: internacion.medicoIngresante || null,
|
||||||
|
motivoEgreso: internacion.motivoEgreso || null,
|
||||||
|
activa: internacion.activa ? 1 : 0,
|
||||||
|
apache: internacion.apache || null,
|
||||||
|
derivacion: internacion.derivacion || null
|
||||||
|
};
|
||||||
|
await db.collection('internaciones').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateInternacion(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
if (key === 'activa') {
|
||||||
|
updateDoc.activa = val ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('internaciones').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteInternacion(id) {
|
||||||
|
await db.collection('internaciones').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== EVOLUCIONES ==========
|
||||||
|
export async function getAllEvoluciones() {
|
||||||
|
const evoluciones = await db.collection('evoluciones').find().toArray();
|
||||||
|
return cleanDocs(evoluciones);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEvolucion(evolucion) {
|
||||||
|
const doc = {
|
||||||
|
id: evolucion.id,
|
||||||
|
internacionId: evolucion.internacionId,
|
||||||
|
fecha: evolucion.fecha,
|
||||||
|
hora: evolucion.hora || '',
|
||||||
|
medico: evolucion.medico || '',
|
||||||
|
signosVitales: JSON.stringify(evolucion.signosVitales || {}),
|
||||||
|
examenFisico: JSON.stringify(evolucion.examenFisico || {}),
|
||||||
|
novedades: evolucion.novedades || '',
|
||||||
|
comentarios: evolucion.comentarios || evolucion.comentario || '',
|
||||||
|
pendientes: evolucion.pendientes || ''
|
||||||
|
};
|
||||||
|
await db.collection('evoluciones').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateEvolucion(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
if (key === 'comentario') {
|
||||||
|
updateDoc.comentarios = val;
|
||||||
|
} else if (key === 'signosVitales' || key === 'examenFisico') {
|
||||||
|
updateDoc[key] = JSON.stringify(val);
|
||||||
|
} else {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('evoluciones').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteEvolucion(id) {
|
||||||
|
await db.collection('evoluciones').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== LABORATORIOS ==========
|
||||||
|
export async function getAllLaboratorios() {
|
||||||
|
const laboratorios = await db.collection('laboratorios').find().toArray();
|
||||||
|
return cleanDocs(laboratorios);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createLaboratorio(laboratorio) {
|
||||||
|
const doc = {
|
||||||
|
id: laboratorio.id,
|
||||||
|
pacienteId: laboratorio.pacienteId,
|
||||||
|
internacionId: laboratorio.internacionId || null,
|
||||||
|
fecha: laboratorio.fecha,
|
||||||
|
hora: laboratorio.hora || null,
|
||||||
|
tipo: laboratorio.tipo,
|
||||||
|
resultados: JSON.stringify(laboratorio.resultados || {}),
|
||||||
|
observaciones: laboratorio.observaciones || null,
|
||||||
|
medicoSolicitante: laboratorio.medicoSolicitante || null
|
||||||
|
};
|
||||||
|
await db.collection('laboratorios').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateLaboratorio(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
if (key === 'resultados') {
|
||||||
|
updateDoc.resultados = JSON.stringify(val);
|
||||||
|
} else {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('laboratorios').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteLaboratorio(id) {
|
||||||
|
await db.collection('laboratorios').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== ACIDOS BASE ==========
|
||||||
|
export async function getAllAcidosBase() {
|
||||||
|
const acidos = await db.collection('acidosbase').find().toArray();
|
||||||
|
return cleanDocs(acidos);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAcidoBase(acido) {
|
||||||
|
const doc = {
|
||||||
|
id: acido.id,
|
||||||
|
pacienteId: acido.pacienteId,
|
||||||
|
internacionId: acido.internacionId || null,
|
||||||
|
fecha: acido.fecha,
|
||||||
|
hora: acido.hora || null,
|
||||||
|
ph: acido.ph !== undefined ? Number(acido.ph) : null,
|
||||||
|
pco2: acido.pco2 !== undefined ? Number(acido.pco2) : null,
|
||||||
|
po2: acido.po2 !== undefined ? Number(acido.po2) : null,
|
||||||
|
hco3: acido.hco3 !== undefined ? Number(acido.hco3) : null,
|
||||||
|
be: acido.be !== undefined ? Number(acido.be) : null,
|
||||||
|
sato2: acido.sato2 !== undefined ? Number(acido.sato2) : null,
|
||||||
|
lactato: acido.lactato !== undefined ? Number(acido.lactato) : null,
|
||||||
|
interpretacion: acido.interpretacion || null,
|
||||||
|
fio2: acido.fio2 !== undefined ? Number(acido.fio2) : null
|
||||||
|
};
|
||||||
|
await db.collection('acidosbase').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAcidoBase(id) {
|
||||||
|
await db.collection('acidosbase').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== CULTIVOS ==========
|
||||||
|
export async function getAllCultivos() {
|
||||||
|
const cultivos = await db.collection('cultivos').find().toArray();
|
||||||
|
return cleanDocs(cultivos);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createCultivo(cultivo) {
|
||||||
|
const doc = {
|
||||||
|
id: cultivo.id,
|
||||||
|
pacienteId: cultivo.pacienteId,
|
||||||
|
internacionId: cultivo.internacionId,
|
||||||
|
fechaToma: cultivo.fechaToma || null,
|
||||||
|
protocolo: cultivo.protocolo || null,
|
||||||
|
fechaResultado: cultivo.fechaResultado || null,
|
||||||
|
tipoMuestra: cultivo.tipoMuestra || null,
|
||||||
|
germen: cultivo.germen || null,
|
||||||
|
sensible: cultivo.sensible || null,
|
||||||
|
resistente: cultivo.resistente || null,
|
||||||
|
estado: cultivo.estado || 'NAF/Pendiente',
|
||||||
|
observaciones: cultivo.observaciones || null
|
||||||
|
};
|
||||||
|
await db.collection('cultivos').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCultivo(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('cultivos').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCultivo(id) {
|
||||||
|
await db.collection('cultivos').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== ESTUDIOS COMPLEMENTARIOS ==========
|
||||||
|
export async function getAllEstudiosComplementarios() {
|
||||||
|
const estudios = await db.collection('estudiosComplementarios').find().toArray();
|
||||||
|
return cleanDocs(estudios);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEstudioComplementario(estudio) {
|
||||||
|
const doc = {
|
||||||
|
id: estudio.id,
|
||||||
|
pacienteId: estudio.pacienteId,
|
||||||
|
internacionId: estudio.internacionId || null,
|
||||||
|
fecha: estudio.fecha || null,
|
||||||
|
tipo: estudio.tipo || null,
|
||||||
|
resultado: estudio.resultado || null
|
||||||
|
};
|
||||||
|
await db.collection('estudiosComplementarios').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteEstudioComplementario(id) {
|
||||||
|
await db.collection('estudiosComplementarios').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== INTERCONSULTAS ==========
|
||||||
|
export async function getAllInterconsultas() {
|
||||||
|
const interconsultas = await db.collection('interconsultas').find().toArray();
|
||||||
|
return cleanDocs(interconsultas).map(ic => ({ ...ic, realizada: !!ic.realizada }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createInterconsulta(ic) {
|
||||||
|
const doc = {
|
||||||
|
id: ic.id,
|
||||||
|
pacienteId: ic.pacienteId,
|
||||||
|
internacionId: ic.internacionId,
|
||||||
|
fecha: ic.fecha || null,
|
||||||
|
servicioInterconsultado: ic.servicioInterconsultado,
|
||||||
|
motivo: ic.motivo || null,
|
||||||
|
respuestaInterconsulta: ic.respuestaInterconsulta || null,
|
||||||
|
respuestaFecha: ic.respuestaFecha || null
|
||||||
|
};
|
||||||
|
await db.collection('interconsultas').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateInterconsulta(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('interconsultas').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteInterconsulta(id) {
|
||||||
|
await db.collection('interconsultas').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== ATB ==========
|
||||||
|
export async function getAllAtb() {
|
||||||
|
const atb = await db.collection('atb').find().toArray();
|
||||||
|
return cleanDocs(atb);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAtb(atb) {
|
||||||
|
const doc = {
|
||||||
|
id: atb.id,
|
||||||
|
pacienteId: atb.pacienteId,
|
||||||
|
internacionId: atb.internacionId,
|
||||||
|
antibiotico: atb.antibiotico,
|
||||||
|
fechaInicio: atb.fechaInicio,
|
||||||
|
fechaFinalizacion: atb.fechaFinalizacion || null
|
||||||
|
};
|
||||||
|
await db.collection('atb').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAtb(id) {
|
||||||
|
await db.collection('atb').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== INDICACIONES ==========
|
||||||
|
export async function getAllIndicaciones() {
|
||||||
|
const indicaciones = await db.collection('indicaciones').find().toArray();
|
||||||
|
return cleanDocs(indicaciones);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createIndicacion(ind) {
|
||||||
|
const doc = {
|
||||||
|
id: ind.id,
|
||||||
|
internacionId: ind.internacionId,
|
||||||
|
tipo: ind.tipo,
|
||||||
|
droga: ind.droga || null,
|
||||||
|
dosis: ind.dosis || null,
|
||||||
|
frecuenciaHoras: ind.frecuenciaHoras || null,
|
||||||
|
via: ind.via || null,
|
||||||
|
tipoPlan: ind.tipoPlan || null,
|
||||||
|
tipoPlan2: ind.tipoPlan2 || null,
|
||||||
|
cantidadMl: ind.cantidadMl || null,
|
||||||
|
cantidadMl2: ind.cantidadMl2 || null,
|
||||||
|
tiempoHoras: ind.tiempoHoras || null,
|
||||||
|
estado: ind.estado || 'Activa',
|
||||||
|
medicoCrea: ind.medicoCrea || null,
|
||||||
|
fechaCrea: ind.fechaCrea || null,
|
||||||
|
tipoInsulina: ind.tipoInsulina || null,
|
||||||
|
unidadesDesayuno: ind.unidadesDesayuno || null,
|
||||||
|
unidadesAlmuerzo: ind.unidadesAlmuerzo || null,
|
||||||
|
unidadesNoche: ind.unidadesNoche || null,
|
||||||
|
indicacionNoFco: ind.indicacionNoFco || null
|
||||||
|
};
|
||||||
|
await db.collection('indicaciones').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateIndicacion(id, datos) {
|
||||||
|
const updateDoc = {};
|
||||||
|
for (const [key, val] of Object.entries(datos)) {
|
||||||
|
if (val !== undefined) {
|
||||||
|
updateDoc[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(updateDoc).length > 0) {
|
||||||
|
await db.collection('indicaciones').updateOne({ id }, { $set: updateDoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteIndicacion(id) {
|
||||||
|
await db.collection('indicaciones').deleteOne({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== MOVIMIENTOS INDICACIONES ==========
|
||||||
|
export async function getAllMovimientosIndicaciones() {
|
||||||
|
const movimientos = await db.collection('movimientos_indicaciones').find().toArray();
|
||||||
|
return cleanDocs(movimientos);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createMovimientoIndicacion(mov) {
|
||||||
|
const doc = {
|
||||||
|
id: mov.id,
|
||||||
|
indicacionId: mov.indicacionId,
|
||||||
|
internacionId: mov.internacionId,
|
||||||
|
tipo: mov.tipo,
|
||||||
|
fecha: mov.fecha,
|
||||||
|
profesional: mov.profesional,
|
||||||
|
indicacionPrevia: mov.indicacionPrevia || null,
|
||||||
|
indicacionNueva: mov.indicacionNueva || null
|
||||||
|
};
|
||||||
|
await db.collection('movimientos_indicaciones').insertOne(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== KV STORE ==========
|
||||||
|
export async function getValue(key) {
|
||||||
|
const doc = await db.collection('kv').findOne({ key });
|
||||||
|
return doc?.value ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setValue(key, value) {
|
||||||
|
await db.collection('kv').updateOne(
|
||||||
|
{ key },
|
||||||
|
{ $set: { key, value } },
|
||||||
|
{ upsert: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
+2
-1
@@ -10,6 +10,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"http-proxy-middleware": "^3.0.5",
|
"http-proxy-middleware": "^3.0.5",
|
||||||
"mariadb": "^3.4.0"
|
"mariadb": "^3.4.0",
|
||||||
|
"mongodb": "^6.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user