summaryrefslogtreecommitdiff
path: root/hosts/ahmed/minecraft-log-server/public
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/ahmed/minecraft-log-server/public')
-rw-r--r--hosts/ahmed/minecraft-log-server/public/index.html11
-rw-r--r--hosts/ahmed/minecraft-log-server/public/scripts/plain.js68
-rw-r--r--hosts/ahmed/minecraft-log-server/public/scripts/reconnecting-eventsource.min.js2
-rw-r--r--hosts/ahmed/minecraft-log-server/public/styles/plain.css7
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; }