diff options
Diffstat (limited to 'hosts/ahmed/minecraft-log-server/public')
4 files changed, 88 insertions, 0 deletions
diff --git a/hosts/ahmed/minecraft-log-server/public/index.html b/hosts/ahmed/minecraft-log-server/public/index.html new file mode 100644 index 0000000..37fdaac --- /dev/null +++ b/hosts/ahmed/minecraft-log-server/public/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <script type="module" src="./scripts/plain.js"></script> + <link rel="stylesheet" href="./styles/plain.css"> + </head> + <body> + <p>System events will appear below.</p> + <pre><output id="target"></output></pre> + </body> +</html> diff --git a/hosts/ahmed/minecraft-log-server/public/scripts/plain.js b/hosts/ahmed/minecraft-log-server/public/scripts/plain.js new file mode 100644 index 0000000..c2da55e --- /dev/null +++ b/hosts/ahmed/minecraft-log-server/public/scripts/plain.js @@ -0,0 +1,68 @@ +import ReconnectingEventSource from "./reconnecting-eventsource.min.js"; + +function main() { + const sse = new ReconnectingEventSource("/stream", { + // Retry time after browser fails to reconnect (e.g. HTTP 502). + // This is pretty sus, so let's wait a little longer... + max_retry_time: 5_000, + }); + + sse.addEventListener("open", (event) => { + addSpecialMessage("info", "Connection to log server established!"); + }); + + sse.addEventListener("error", (event) => { + console.error("SSE Error: ", event); + addSpecialMessage("error", "Connection to log server lost! Retrying connection..."); + }); + + sse.addEventListener("entry", (event) => { + const line = JSON.parse(event.data); + addEntry(line); + }); +} + +function addEntry(json) { + const $container = document.createElement("span"); + $container.classList.add("regular"); + + const $time = document.createElement("time"); + const timestamp = new Date(+json["__REALTIME_TIMESTAMP"] / 1000); + $time.textContent = `[${timestamp.toISOString()}]: `; + $time.dateTime = timestamp; + $container.append($time) + + const $unit = document.createElement("span"); + $unit.textContent = json["_SYSTEMD_UNIT"]; + $container.append($unit); + $container.append(": ") + + const $message = document.createElement("span"); + $message.textContent = json["MESSAGE"]; + $container.append($message); + + $container.append("\n"); + addToOutput($container); +} + +function addSpecialMessage(klass, message) { + const $message = document.createElement("span"); + $message.classList.add(klass); + $message.textContent = message; + $message.textContent += "\n"; + addToOutput($message); +} + +function addToOutput($elem) { + // TODO: Maybe allow for a little wiggle-room? + const wasAtBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight; + + const $target = document.getElementById("target"); + $target.appendChild($elem); + + if (wasAtBottom) { + window.scrollTo(0, document.body.scrollHeight); + } +} + +main(); diff --git a/hosts/ahmed/minecraft-log-server/public/scripts/reconnecting-eventsource.min.js b/hosts/ahmed/minecraft-log-server/public/scripts/reconnecting-eventsource.min.js new file mode 100644 index 0000000..344e759 --- /dev/null +++ b/hosts/ahmed/minecraft-log-server/public/scripts/reconnecting-eventsource.min.js @@ -0,0 +1,2 @@ +// https://github.com/fanout/reconnecting-eventsource +export class EventSourceNotAvailableError extends Error{constructor(){super("EventSource not available.\nConsider loading an EventSource polyfill and making it available globally as EventSource, or passing one in as eventSourceClass to the ReconnectingEventSource constructor.")}}export default class e{_configuration;CONNECTING=0;OPEN=1;CLOSED=2;static CONNECTING=0;static OPEN=1;static CLOSED=2;_eventSource;_lastEventId;_timer;_listeners;_onevent_wrapped;readyState;url;withCredentials;max_retry_time;eventSourceClass;constructor(e,t){if(this._configuration=null!=t?Object.assign({},t):void 0,this.withCredentials=!1,this._eventSource=null,this._lastEventId=null,this._timer=null,this._listeners={},this.url=e.toString(),this.readyState=this.CONNECTING,this.max_retry_time=3e3,this.eventSourceClass=globalThis.EventSource,null!=this._configuration&&(this._configuration.lastEventId&&(this._lastEventId=this._configuration.lastEventId,delete this._configuration.lastEventId),this._configuration.max_retry_time&&(this.max_retry_time=this._configuration.max_retry_time,delete this._configuration.max_retry_time),this._configuration.eventSourceClass&&(this.eventSourceClass=this._configuration.eventSourceClass,delete this._configuration.eventSourceClass)),null==this.eventSourceClass||"function"!=typeof this.eventSourceClass)throw new EventSourceNotAvailableError;this._onevent_wrapped=e=>{this._onevent(e)},this._start()}dispatchEvent(e){throw Error("Method not implemented.")}_start(){let e=this.url;for(let t of(this._lastEventId&&(-1===e.indexOf("?")?e+="?":e+="&",e+="lastEventId="+encodeURIComponent(this._lastEventId)),this._eventSource=new this.eventSourceClass(e,this._configuration),this._eventSource.onopen=e=>{this._onopen(e)},this._eventSource.onerror=e=>{this._onerror(e)},this._eventSource.onmessage=e=>{this.onmessage(e)},Object.keys(this._listeners)))this._eventSource.addEventListener(t,this._onevent_wrapped)}_onopen(e){0===this.readyState&&(this.readyState=1,this.onopen(e))}_onerror(e){if(1===this.readyState&&(this.readyState=0,this.onerror(e)),this._eventSource&&2===this._eventSource.readyState){this._eventSource.close(),this._eventSource=null;let t=Math.round(this.max_retry_time*Math.random());this._timer=setTimeout(()=>this._start(),t)}}_onevent(e){e instanceof MessageEvent&&(this._lastEventId=e.lastEventId);let t=this._listeners[e.type];if(null!=t)for(let s of[...t])s.call(this,e);"message"===e.type&&this.onmessage(e)}onopen(e){}onerror(e){}onmessage(e){}close(){this._timer&&(clearTimeout(this._timer),this._timer=null),this._eventSource&&(this._eventSource.close(),this._eventSource=null),this.readyState=2}addEventListener(e,t,s){e in this._listeners||(this._listeners[e]=[],null!=this._eventSource&&this._eventSource.addEventListener(e,this._onevent_wrapped));let n=this._listeners[e];Array.isArray(n)&&!n.includes(t)&&n.push(t)}removeEventListener(e,t,s){let n=this._listeners[e];if(null!=n){for(;;){let i=n.indexOf(t);if(-1===i)break;n.splice(i,1)}n.length<=0&&(delete this._listeners[e],null!=this._eventSource&&this._eventSource.removeEventListener(e,this._onevent_wrapped))}}}; diff --git a/hosts/ahmed/minecraft-log-server/public/styles/plain.css b/hosts/ahmed/minecraft-log-server/public/styles/plain.css new file mode 100644 index 0000000..a40b612 --- /dev/null +++ b/hosts/ahmed/minecraft-log-server/public/styles/plain.css @@ -0,0 +1,7 @@ +/* Make special messages stand out */ +.error, .info { font-family: serif; font-style: italic; } +.error { color: red; } +.info { color: blue; } + +/* Make time lower visual priority */ +time { color: grey; } |