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