2026-03-26 16:52:54 +07:00
|
|
|
const express = require('express');
|
2026-04-06 16:50:17 +07:00
|
|
|
const config = require('../config/app');
|
2026-03-26 16:52:54 +07:00
|
|
|
const logger = require('./utils/logger');
|
|
|
|
|
const migrate = require('./storage/migrate');
|
|
|
|
|
const { createHttpJsonConnector } = require('./connectors/httpJsonConnector');
|
|
|
|
|
const { createHl7TcpConnector } = require('./connectors/hl7TcpConnector');
|
|
|
|
|
const { createAstmSerialConnector } = require('./connectors/astmSerialConnector');
|
|
|
|
|
const { processMessage } = require('./pipeline/workflow');
|
|
|
|
|
const { startWorker, stopWorker } = require('./pipeline/deliveryWorker');
|
|
|
|
|
const instrumentService = require('./instrumentConfig/service');
|
2026-04-06 16:50:17 +07:00
|
|
|
const { validateAndLoadInstrumentConfigs } = require('./instrumentConfig/validator');
|
2026-03-26 16:52:54 +07:00
|
|
|
const { createHealthRouter } = require('./routes/health');
|
|
|
|
|
const { router: instrumentRouter } = require('./routes/instrumentConfig');
|
|
|
|
|
const metricsRouter = require('./routes/metrics');
|
|
|
|
|
|
|
|
|
|
async function bootstrap() {
|
2026-04-06 16:50:17 +07:00
|
|
|
validateAndLoadInstrumentConfigs();
|
2026-03-26 16:52:54 +07:00
|
|
|
await migrate();
|
|
|
|
|
await instrumentService.init();
|
|
|
|
|
|
2026-04-06 16:50:17 +07:00
|
|
|
const connectorFactories = {
|
|
|
|
|
'http-json': createHttpJsonConnector,
|
|
|
|
|
'hl7-tcp': createHl7TcpConnector,
|
|
|
|
|
'astm-serial': createAstmSerialConnector
|
|
|
|
|
};
|
|
|
|
|
const connectors = instrumentService.list()
|
|
|
|
|
.filter((entry) => entry.enabled)
|
|
|
|
|
.map((entry) => {
|
|
|
|
|
const createConnector = connectorFactories[entry.connector];
|
|
|
|
|
if (!createConnector) {
|
|
|
|
|
logger.warn({ connector: entry.connector, instrument_id: entry.instrument_id }, 'unknown connector in instrument config, skipping startup');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return createConnector({
|
|
|
|
|
...(entry.connectorConfig || {}),
|
|
|
|
|
instrument_id: entry.instrument_id
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
|
|
|
|
if (!connectors.length) {
|
|
|
|
|
logger.warn('no enabled connectors configured, ingestion listeners are disabled');
|
|
|
|
|
}
|
2026-03-26 16:52:54 +07:00
|
|
|
|
|
|
|
|
connectors.forEach((connector) => {
|
2026-04-06 16:50:17 +07:00
|
|
|
connector.onMessage(async (incoming) => {
|
2026-03-26 16:52:54 +07:00
|
|
|
try {
|
2026-04-06 16:50:17 +07:00
|
|
|
const payload = incoming && Object.prototype.hasOwnProperty.call(incoming, 'payload')
|
|
|
|
|
? incoming.payload
|
|
|
|
|
: incoming;
|
|
|
|
|
const context = incoming && incoming.context ? incoming.context : {};
|
|
|
|
|
await processMessage(connector.name(), payload, context);
|
2026-03-26 16:52:54 +07:00
|
|
|
} catch (err) {
|
|
|
|
|
logger.error({ err: err.message, connector: connector.name() }, 'pipeline error');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
connector.onError((err) => {
|
|
|
|
|
logger.error({ err: err.message }, `${connector.name()} emitted error`);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Promise.all(connectors.map((connector) => connector.start()));
|
|
|
|
|
await startWorker();
|
|
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
app.use('/health', createHealthRouter(connectors));
|
|
|
|
|
app.use('/instruments', instrumentRouter);
|
|
|
|
|
app.use('/metrics', metricsRouter);
|
|
|
|
|
app.listen(config.healthPort, () => {
|
|
|
|
|
logger.info({ port: config.healthPort }, 'health server ready');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
process.on('SIGINT', async () => {
|
|
|
|
|
logger.info('shutdown signal received');
|
|
|
|
|
await shutdown(connectors);
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|
|
|
|
|
process.on('SIGTERM', async () => {
|
|
|
|
|
logger.info('terminate signal received');
|
|
|
|
|
await shutdown(connectors);
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function shutdown(connectors) {
|
|
|
|
|
await stopWorker();
|
|
|
|
|
await Promise.all(connectors.map((connector) => connector.stop()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bootstrap().catch((err) => {
|
|
|
|
|
logger.fatal({ err: err.message }, 'failed to start middleware');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|