From 9a42fc047f0361e4a2ff7af917a6ed77e0d2da09 Mon Sep 17 00:00:00 2001 From: marlon-erler Date: Sun, 7 Jul 2024 18:16:28 +0200 Subject: [PATCH] cleanup, add comments, improve output --- config-handler.ts | 1 + index.ts | 24 ++++++++++------- utility.ts | 20 +++++++------- websocket-handler.ts | 62 ++++++++++++++++++++++++-------------------- 4 files changed, 60 insertions(+), 47 deletions(-) diff --git a/config-handler.ts b/config-handler.ts index 6c20752..fa197fc 100644 --- a/config-handler.ts +++ b/config-handler.ts @@ -18,6 +18,7 @@ export async function getConfig(): Promise { const configString = await Bun.file(configPath).text(); const configObject = JSON.parse(configString); + // validate config file Object.keys(defaultConfig).forEach((key) => { if (!configObject[key]) throw null; }); diff --git a/index.ts b/index.ts index ec0f05d..bdfe779 100755 --- a/index.ts +++ b/index.ts @@ -1,13 +1,13 @@ #!/usr/bin/env bun import { - addConnection, connectServers, forgetConnection, getWebSocketStats, processMessage, + trackConnection, } from "./websocket-handler"; -import { writeInfo, writeStat, writeSuccess } from "./utility"; +import { writeStatNumber, writeStatString, writeSuccess } from "./utility"; import Colors from "colors"; import IP from "ip"; @@ -16,8 +16,8 @@ import { ServerWebSocket } from "bun"; import { getConfig } from "./config-handler"; // CONFIG -const staticDir = Path.join(Path.dirname(import.meta.path), "static/dist"); -const defaultPage = "index.html"; +const STATIC_DIR = Path.join(Path.dirname(import.meta.path), "static/dist"); +const DEFAULT_PAGE = "index.html"; // TYPES export type WS = ServerWebSocket | WebSocket; @@ -39,7 +39,7 @@ async function main() { const requestPath = new URL(request.url).pathname; switch (requestPath) { case "/": - return createFileResponse(defaultPage); + return createFileResponse(DEFAULT_PAGE); case "/stats": return new Response(JSON.stringify(getAllStats())); default: @@ -49,7 +49,7 @@ async function main() { websocket: { open(ws) { - addConnection(ws); + trackConnection(ws); }, message(ws, message) { processMessage(ws, message.toString(), config); @@ -61,7 +61,7 @@ async function main() { }); function createFileResponse(requestPath: string): Response { - const fullPath = Path.join(staticDir, requestPath); + const fullPath = Path.join(STATIC_DIR, requestPath); return new Response(Bun.file(fullPath)); } @@ -82,21 +82,25 @@ async function main() { // CLI writeSuccess(`###\nstarted up ${new Date().toLocaleString()}`); - console.log("relevant output available in the 'logs' file"); function updateCLI() { console.clear(); + console.log(Colors.bold.bgWhite("UNIVERSAL DECENTRALIZED NETWORK")); + console.log(Colors.yellow("relevant output is available in the 'logs' file")); console.log(); console.log(new Date().toLocaleString()); console.log(); - getServerStats().forEach((entry) => writeInfo(...entry)); + getServerStats().forEach((entry) => writeStatString(...entry)); + console.log(); + + getWebSocketStats().forEach((entry) => writeStatNumber(...entry)); console.log(); - getWebSocketStats().forEach((entry) => writeStat(...entry)); } + setTimeout(updateCLI, 1000); setInterval(updateCLI, 5000); } diff --git a/utility.ts b/utility.ts index 196a381..cbdbde7 100644 --- a/utility.ts +++ b/utility.ts @@ -3,10 +3,10 @@ import Fs from "fs/promises"; import { WebSocketMessage } from "./websocket-handler"; // CONFIG -const indent = 25; +const STAT_INDENT = 25; -// METHODS -export function guardStringNotEmpty( +// SAFETY +export function doIfIsString( data: any, cb: (data: string) => void ): void { @@ -14,21 +14,23 @@ export function guardStringNotEmpty( cb(data); } -// writing +// OUTPUT async function writeToLogFile(message: string): Promise { await Fs.appendFile("logs", `${message}\n`); } -export function writeInfo(label: string, value: string): void { - console.log(`${label.padEnd(indent, " ")} ${Colors.bold.green(value)}`); +// stats +export function writeStatString(label: string, value: string): void { + console.log(`${label.padEnd(STAT_INDENT, " ")} ${Colors.bold.green(value)}`); } -export function writeStat(label: string, value: number): void { +export function writeStatNumber(label: string, value: number): void { console.log( - `${label.padEnd(indent, " ")} ${Colors.bold.blue(value.toString())}` + `${label.padEnd(STAT_INDENT, " ")} ${Colors.bold.blue(value.toString())}` ); } +// logging export function writeError(message: string): void { console.log(Colors.bold.red(message)); writeToLogFile(message); @@ -39,7 +41,7 @@ export function writeSuccess(message: string): void { writeToLogFile(message); } -// messages +// MESSAGES export function stringifyMessage(messageObject: WebSocketMessage): string { return JSON.stringify(messageObject); } diff --git a/websocket-handler.ts b/websocket-handler.ts index fdff8fa..22246bb 100644 --- a/websocket-handler.ts +++ b/websocket-handler.ts @@ -1,5 +1,5 @@ import { - guardStringNotEmpty, + doIfIsString, parseMessage, stringifyMessage, writeError, @@ -30,7 +30,7 @@ export interface WebSocketMessage { // TRACKING const knownMessageIds = new Set(); -export const subscriptions = new SubscriptionMap(); +export const subscriptionMap = new SubscriptionMap(); export const clientsConnected = new Set(); export const serversConnectedAsClient = new Set(); export const serversConnected = new Set(); @@ -39,28 +39,28 @@ export function getServerCount() { return serversConnected.size + serversConnectedAsClient.size; } -export function addConnection(ws: WS) { +export function trackConnection(ws: WS) { clientsConnected.add(ws); } -export function trackSubscription(ws: WS, channel: string): void { - subscriptions.set(ws, channel); - confirmSubscription(ws, channel); -} - -export function removeSubscription(ws: WS, channel: string): void { - subscriptions.delete(ws, channel); - confirmSubscription(ws, channel); -} - export function forgetConnection(ws: WS): void { clientsConnected.delete(ws); - const channels = subscriptions.getChannelList(ws); + const channels = subscriptionMap.getChannelList(ws); channels?.forEach((channel) => { - subscriptions.delete(ws, channel); + subscriptionMap.delete(ws, channel); }); } +export function trackSubscription(ws: WS, channel: string): void { + subscriptionMap.set(ws, channel); + confirmSubscription(ws, channel); +} + +export function forgetSubscription(ws: WS, channel: string): void { + subscriptionMap.delete(ws, channel); + confirmSubscription(ws, channel); +} + // OTHER SERVERS export function connectServers(config: typeof defaultConfig): void { config.connectedServers.forEach((serverAddress) => { @@ -73,9 +73,11 @@ export function connectServer( config: typeof defaultConfig ): void { try { + // connect const ws = new WebSocket(address); serversConnected.add(ws); + // listen ws.addEventListener("open", () => { writeSuccess(`connected to server at "${address}".`); @@ -113,7 +115,7 @@ export function subscribeChannel(server: WS, channel: string): void { // AUDIT export function getWebSocketStats(): [string, number][] { return [ - ["channels", subscriptions.clientsPerChannel.size], + ["channels", subscriptionMap.clientsPerChannel.size], ["clients connected", clientsConnected.size], ["servers connected", getServerCount()], ]; @@ -125,27 +127,31 @@ export function processMessage( messageString: string, config: typeof defaultConfig ): void { + // check id const messageObject: WebSocketMessage = parseMessage(messageString); - if (!messageObject.uuid) { + if (!messageObject.uuid) { // assign if no id messageObject.uuid = Crypto.randomUUID(); - } else { + } else { // ignore if already seen if (knownMessageIds.has(messageObject.uuid)) return; } + // remember message knownMessageIds.add(messageObject.uuid); - guardStringNotEmpty(messageObject.subscribeChannel, (subscribeChannel) => { + // subscriptions + doIfIsString(messageObject.subscribeChannel, (subscribeChannel) => { trackSubscription(ws, subscribeChannel); }); - guardStringNotEmpty( - messageObject.unsubscribeChannel, - (unsubscribeChannel) => { - removeSubscription(ws, unsubscribeChannel); - } - ); - guardStringNotEmpty(messageObject.messageChannel, (messageChannel) => { + doIfIsString(messageObject.unsubscribeChannel, (unsubscribeChannel) => { + forgetSubscription(ws, unsubscribeChannel); + }); + + // message + doIfIsString(messageObject.messageChannel, (messageChannel) => { if (messageObject.messageBody == undefined) return; sendMessage(messageChannel, messageObject.messageBody, messageObject.uuid!); }); + + // other server if (messageObject.requestingServerConnection == true) { clientsConnected.delete(ws); serversConnectedAsClient.add(ws); @@ -165,7 +171,7 @@ export function sendMessage( const messageString = stringifyMessage(messageObject); channels.forEach((channel) => { - const subscribers = subscriptions.getClientList(channel); + const subscribers = subscriptionMap.getClientList(channel); subscribers?.forEach((ws) => ws.send(messageString)); }); } @@ -181,7 +187,7 @@ function requestServerConnection(ws: WS): void { function confirmSubscription(ws: WS, messageChannel: string): void { const messageObject = { messageChannel, - subscribed: subscriptions.getChannelList(ws) ? true : false, + subscribed: subscriptionMap.getChannelList(ws) ? true : false, }; const messageString = stringifyMessage(messageObject); ws.send(messageString);