diff --git a/index.ts b/index.ts index 62c72b3..11967a3 100644 --- a/index.ts +++ b/index.ts @@ -1,11 +1,11 @@ import { ServerWebSocket, write } from "bun"; import { addConnection, - audit, connectServer, connectServers, connectedClientCount, forgetConnection, + getStats, processMessage, subscribeChannel, subscriptions, @@ -42,6 +42,8 @@ async function main() { switch (requestPath) { case "/": return createFileResponse(defaultPage); + case "/stats": + return new Response(JSON.stringify(getStats())); default: return createFileResponse(requestPath); } @@ -83,7 +85,7 @@ async function main() { writeInfo("server ip address", Colors.bold.green(IP.address())); console.log(); - audit().forEach((entry) => writeStat(...entry)); + getStats().forEach((entry) => writeStat(...entry)); } setInterval(updateCLI, 5000); diff --git a/static/dist/index.js b/static/dist/index.js index 30a7b19..26f8541 100644 --- a/static/dist/index.js +++ b/static/dist/index.js @@ -186,6 +186,7 @@ var staticTextEnglish = { channel: "Channel", channel_placeholder: "my-channel", + info: "Info", message: "Message", messages: "Messages", message_placeholder: "Hello, world!", @@ -194,6 +195,7 @@ messageLastReceived: "Last received message", messagesReceived: "Received Messages", send: "Send", + serverInfo: "Server Info", settings: "Settings", subscribeToChannel: "Subscribe to channel", subscribe: "Subscribe", @@ -204,6 +206,7 @@ es: { channel: "Canal", channel_placeholder: "mi-canal", + info: "Info", message: "Mensaje", messages: "Mensajes", message_placeholder: "Hola!", @@ -212,6 +215,7 @@ messageLastReceived: "\xDCltimo mensaje", messagesReceived: "Todos los Mensajes", send: "Enviar", + serverInfo: "Informaci\xF3n", settings: "Configuraci\xF3n", subscribeToChannel: "Suscribirse a un canal", subscribe: "Suscribirse", @@ -220,6 +224,7 @@ de: { channel: "Kanal", channel_placeholder: "mein-kanal", + info: "Daten", message: "Nachricht", messages: "Nachrichten", message_placeholder: "Hallo!", @@ -228,6 +233,7 @@ messageLastReceived: "Letzte Nachrichte", messagesReceived: "Empfangene Nachrichten", send: "Senden", + serverInfo: "Serverdaten", settings: "Einstellungen", subscribeToChannel: "Kanal Abonnieren", subscribe: "Abonnieren", @@ -250,6 +256,7 @@ uuid = new UUID(); }; var ws = new WebSocket(`ws://${window.location.host}`); + ws.addEventListener("open", () => updateStats()); ws.addEventListener("message", (message) => { const formatted = formatMessage(message); const { messageChannel, messageBody } = parseMessage(message); @@ -265,6 +272,19 @@ var lastReceivedMessage = new State( getText("noMessagesReceived") ); + var statHTMLString = new State(""); + async function updateStats() { + const response = await fetch( + `${window.location.protocol}//${window.location.host}/stats` + ); + const data = await response.json(); + let html = ""; + data.forEach((item) => { + const tile = /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "secondary flex width-100" }, item[0].toString()), /* @__PURE__ */ createElement("b", null, item[1].toString())); + html += tile.outerHTML; + }); + statHTMLString.value = html; + } var subscriptionChannel = new State(""); var newMessageChannel = new State(""); var newMessageBody = new State(""); @@ -293,6 +313,7 @@ sendToWS({ unsubscribeChannel: subscriptionChannel.value }); } function sendMessage() { + if (isMessageEmpty.value == true) return; sendToWS({ messageChannel: newMessageChannel.value, messageBody: newMessageBody.value @@ -300,6 +321,11 @@ newMessageBody.value = ""; } + // src/infoScreen.tsx + function InfoScreen() { + return /* @__PURE__ */ createElement("article", { id: "info-screen" }, /* @__PURE__ */ createElement("header", null, getText("serverInfo"), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement("button", { "on:click": updateStats }, /* @__PURE__ */ createElement("span", { class: "icon" }, "refresh")))), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "flex-column gap", "subscribe:innerHTML": statHTMLString }))); + } + // src/mainScreen.tsx function MainScreen() { return /* @__PURE__ */ createElement("article", { id: "main-screen" }, /* @__PURE__ */ createElement("header", null, getText("settings")), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "tile width-input" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, getText("messageLastReceived")), /* @__PURE__ */ createElement("p", { class: "secondary", "subscribe:innerText": lastReceivedMessage }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, getText("subscribeToChannel")), /* @__PURE__ */ createElement( @@ -367,7 +393,7 @@ // src/index.tsx document.body.prepend( - /* @__PURE__ */ createElement("menu", null, /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#main-screen", active: true }, /* @__PURE__ */ createElement("span", { class: "icon" }, "settings"), getText("settings")), /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#message-screen" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "chat"), getText("messages"))) + /* @__PURE__ */ createElement("menu", null, /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#info-screen", active: true }, /* @__PURE__ */ createElement("span", { class: "icon" }, "info"), getText("info")), /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#main-screen" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "settings"), getText("settings")), /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#message-screen" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "chat"), getText("messages"))) ); - document.querySelector("main").append(MainScreen(), MessageScreen()); + document.querySelector("main").append(InfoScreen(), MainScreen(), MessageScreen()); })(); diff --git a/static/src/index.tsx b/static/src/index.tsx index 4437fd1..1dc3e33 100644 --- a/static/src/index.tsx +++ b/static/src/index.tsx @@ -1,5 +1,6 @@ import * as React from "bloatless-react"; +import { InfoScreen } from "./infoScreen"; import { MainScreen } from "./mainScreen"; import { MessageScreen } from "./messageScreen"; import { getText } from "./translations"; @@ -7,7 +8,11 @@ import { getText } from "./translations"; // WS document.body.prepend( - + + info + {getText("info")} + + settings {getText("settings")} @@ -17,4 +22,6 @@ document.body.prepend( ); -document.querySelector("main")!.append(MainScreen(), MessageScreen()); +document + .querySelector("main")! + .append(InfoScreen(), MainScreen(), MessageScreen()); diff --git a/static/src/infoScreen.tsx b/static/src/infoScreen.tsx new file mode 100644 index 0000000..6555d7a --- /dev/null +++ b/static/src/infoScreen.tsx @@ -0,0 +1,23 @@ +import * as React from "bloatless-react"; + +import { statHTMLString, updateStats } from "./model"; + +import { getText } from "./translations"; + +export function InfoScreen() { + return ( +
+
+ {getText("serverInfo")} + + + +
+
+
+
+
+ ); +} diff --git a/static/src/model.tsx b/static/src/model.tsx index 07c5715..1ba7423 100644 --- a/static/src/model.tsx +++ b/static/src/model.tsx @@ -11,6 +11,8 @@ export class Message implements React.Identifiable { // WS const ws = new WebSocket(`ws://${window.location.host}`); +ws.addEventListener("open", () => updateStats()); + ws.addEventListener("message", (message) => { const formatted = formatMessage(message); const { messageChannel, messageBody } = parseMessage(message); @@ -25,11 +27,34 @@ ws.addEventListener("message", (message) => { }); // STATE +// messages export const messages = new React.ListState(); export const lastReceivedMessage = new React.State( getText("noMessagesReceived") ); +// stats +export const statHTMLString = new React.State(""); +export async function updateStats() { + const response = await fetch( + `${window.location.protocol}//${window.location.host}/stats` + ); + const data: [string, string][] = await response.json(); + + let html = ""; + data.forEach((item) => { + const tile = ( +
+ {item[0].toString()} + {item[1].toString()} +
+ ); + html += tile.outerHTML; + }); + statHTMLString.value = html; +} + +// elements export const subscriptionChannel = new React.State(""); export const newMessageChannel = new React.State(""); @@ -67,6 +92,7 @@ export function unsubscribe() { } export function sendMessage() { + if (isMessageEmpty.value == true) return; sendToWS({ messageChannel: newMessageChannel.value, messageBody: newMessageBody.value, diff --git a/static/src/translations.tsx b/static/src/translations.tsx index 6cdf7ac..c776f34 100644 --- a/static/src/translations.tsx +++ b/static/src/translations.tsx @@ -2,6 +2,7 @@ export const staticTextEnglish = { channel: "Channel", channel_placeholder: "my-channel", + info: "Info", message: "Message", messages: "Messages", message_placeholder: "Hello, world!", @@ -10,6 +11,7 @@ export const staticTextEnglish = { messageLastReceived: "Last received message", messagesReceived: "Received Messages", send: "Send", + serverInfo: "Server Info", settings: "Settings", subscribeToChannel: "Subscribe to channel", subscribe: "Subscribe", @@ -21,6 +23,7 @@ export const translations: { [key: string]: typeof staticTextEnglish } = { es: { channel: "Canal", channel_placeholder: "mi-canal", + info: "Info", message: "Mensaje", messages: "Mensajes", message_placeholder: "Hola!", @@ -29,6 +32,7 @@ export const translations: { [key: string]: typeof staticTextEnglish } = { messageLastReceived: "Ültimo mensaje", messagesReceived: "Todos los Mensajes", send: "Enviar", + serverInfo: "Información", settings: "Configuración", subscribeToChannel: "Suscribirse a un canal", subscribe: "Suscribirse", @@ -37,6 +41,7 @@ export const translations: { [key: string]: typeof staticTextEnglish } = { de: { channel: "Kanal", channel_placeholder: "mein-kanal", + info: "Daten", message: "Nachricht", messages: "Nachrichten", message_placeholder: "Hallo!", @@ -45,6 +50,7 @@ export const translations: { [key: string]: typeof staticTextEnglish } = { messageLastReceived: "Letzte Nachrichte", messagesReceived: "Empfangene Nachrichten", send: "Senden", + serverInfo: "Serverdaten", settings: "Einstellungen", subscribeToChannel: "Kanal Abonnieren", subscribe: "Abonnieren", diff --git a/websocket-handler.ts b/websocket-handler.ts index cda75c6..57ea448 100644 --- a/websocket-handler.ts +++ b/websocket-handler.ts @@ -109,7 +109,7 @@ export function subscribeChannel(server: WS, channel: string): void { } // AUDIT -export function audit(): [string, number][] { +export function getStats(): [string, number][] { return [ ["channels", subscriptions.clientsPerChannel.size], ["clients connected", connectedClientCount],