Initial commit
This commit is contained in:
commit
d9cff2e70c
72 changed files with 2878 additions and 0 deletions
160
packages/server/index.ts
Normal file
160
packages/server/index.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { WebSocketServer } from 'ws';
|
||||
import { type Message, MessageType } from '@hnu.de/hl7v2-shared';
|
||||
import 'dotenv/config';
|
||||
import { config } from './config';
|
||||
|
||||
|
||||
// ###########################################################################
|
||||
// ### ID Pool
|
||||
// ###########################################################################
|
||||
|
||||
let availableIds: string[] = [];
|
||||
|
||||
for (const prefix of config.prefixes) {
|
||||
for (let i = 1; i <= config.poolSize / config.prefixes.length; i++) {
|
||||
const number = i.toString().padStart(config.poolSize.toString().length, '0');
|
||||
availableIds.push(`${prefix}-${number}`);
|
||||
}
|
||||
}
|
||||
|
||||
function shake(arr: any[]) {
|
||||
for (let i = arr.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[arr[i], arr[j]] = [arr[j], arr[i]];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
availableIds = shake(availableIds);
|
||||
|
||||
|
||||
// ###########################################################################
|
||||
// ### Websocket Server
|
||||
// ###########################################################################
|
||||
|
||||
const wss = new WebSocketServer({ port: config.port });
|
||||
|
||||
const clients = new Map();
|
||||
|
||||
console.log(`Starting WebSocket server ...`);
|
||||
|
||||
console.log("Server configuration:", config);
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
|
||||
// check for available IDs
|
||||
if (availableIds.length === 0) {
|
||||
console.log('Connection rejected: No available IDs.');
|
||||
ws.close(1013, 'Server is full. Please try again later.'); // 1013: Try again later
|
||||
return;
|
||||
}
|
||||
|
||||
// get ID from pool and assign to user
|
||||
const userId = availableIds.pop();
|
||||
if (!userId) {
|
||||
console.log('Connection rejected: Failed to retrieve ID.');
|
||||
ws.close(1013, 'Server is full. Please try again later.'); // 1013: Try again later
|
||||
return;
|
||||
}
|
||||
|
||||
// store client in map
|
||||
clients.set(userId, ws);
|
||||
|
||||
setTimeout(() => {
|
||||
// send user ID to client
|
||||
const welcomeMessage = {
|
||||
type: 'assign_id',
|
||||
payload: {
|
||||
userId: userId,
|
||||
},
|
||||
} as Message;
|
||||
ws.send(JSON.stringify(welcomeMessage));
|
||||
|
||||
console.log(`Client connected. Assigning ID: ${userId}`);
|
||||
}, 0);
|
||||
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
|
||||
const parsedMessage: Message = JSON.parse(message.toString());
|
||||
console.log(`Received message from ${userId}:`, parsedMessage);
|
||||
|
||||
// We only expect one type of message from clients: 'send_hl7v2'
|
||||
if (parsedMessage.type === MessageType.send_hl7v2) {
|
||||
|
||||
const { message } = parsedMessage.payload;
|
||||
// TODO: validate message
|
||||
|
||||
// get sender and recipient ID
|
||||
const recipientId = parseMshField(message, 5);
|
||||
if (!recipientId) {
|
||||
const errorMessage = {
|
||||
type: 'delivery_error',
|
||||
payload: {
|
||||
error: `Message is missing header field 5.`,
|
||||
},
|
||||
} as Message;
|
||||
ws.send(JSON.stringify(errorMessage));
|
||||
}
|
||||
|
||||
// Find the recipient's WebSocket connection in our map.
|
||||
const recipientWs = clients.get(recipientId);
|
||||
|
||||
if (recipientWs && recipientWs.readyState === WebSocket.OPEN) {
|
||||
|
||||
// The recipient is connected. Forward the message.
|
||||
const forwardMessage = {
|
||||
type: MessageType.receive_hl7v2,
|
||||
payload: {
|
||||
message: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
} as Message;
|
||||
recipientWs.send(JSON.stringify(forwardMessage));
|
||||
console.log(`Forwarded message to ${recipientId}`);
|
||||
|
||||
} else {
|
||||
|
||||
// The recipient is not connected or not found.
|
||||
console.log(`Recipient ${recipientId} not found or not connected.`);
|
||||
const errorMessage = {
|
||||
type: MessageType.delivery_error,
|
||||
payload: {
|
||||
error: `Station with ID ${recipientId} is not available.`,
|
||||
},
|
||||
} as Message;
|
||||
ws.send(JSON.stringify(errorMessage));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to process message from ${userId}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// listen to disconnects to free IDs
|
||||
ws.on('close', () => {
|
||||
console.log(`Client ${userId} disconnected.`);
|
||||
clients.delete(userId);
|
||||
if (userId) {
|
||||
availableIds.push(userId);
|
||||
}
|
||||
availableIds = shake(availableIds);
|
||||
});
|
||||
|
||||
ws.on('error', (error) => {
|
||||
console.error(`WebSocket error for client ${userId}:`, error);
|
||||
});
|
||||
});
|
||||
|
||||
function parseMshField(message: string, fieldIndex: number) {
|
||||
try {
|
||||
const mshLine = message.split(/[\r\n]+/)[0];
|
||||
if (!mshLine || !mshLine.startsWith('MSH')) return null;
|
||||
const fields = mshLine.split('|');
|
||||
return fields[fieldIndex] || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`WebSocket server started on port ${config.port}`);
|
Loading…
Add table
Add a link
Reference in a new issue