summaryrefslogtreecommitdiffstats
path: root/assets
diff options
context:
space:
mode:
Diffstat (limited to 'assets')
-rw-r--r--assets/index.css61
-rw-r--r--assets/index.html30
-rw-r--r--assets/index.js87
3 files changed, 178 insertions, 0 deletions
diff --git a/assets/index.css b/assets/index.css
new file mode 100644
index 0000000..9c170e4
--- /dev/null
+++ b/assets/index.css
@@ -0,0 +1,61 @@
+html {
+ background: var(--bg);
+ color: var(--fg);
+ font-size: 12pt;
+ font-family: monospace;
+ display: flex;
+ flex-direction: column;
+}
+body {
+ height: calc(100vh - 1rem);
+ width: calc(100vw - 4rem);
+ max-width: 640px;
+ margin: 0 2rem 1rem;
+ line-height: 1.4;
+ align-self: center;
+}
+main {
+ display: flex;
+ flex-direction: column;
+}
+hgroup {
+ text-align: center;
+}
+table {
+ width: 100%;
+ max-width: 500px;
+ align-self: center;
+ border-collapse: collapse;
+}
+tr {
+ border: 2px solid #ddd;
+}
+td {
+ padding: 5px;
+}
+td:first-of-type + td {
+ text-align: right;
+}
+.ok {
+ color: #228b22;
+}
+.warning {
+ color: #ff8c00;
+}
+.error {
+ color: #dc143c;
+}
+
+#status {
+ color: #fff;
+ background: #228b22;
+}
+#status.ok {
+ background: #228b22;
+}
+#status.warning {
+ background: #ff8c00;
+}
+#status.error {
+ background: #dc143c;
+}
diff --git a/assets/index.html b/assets/index.html
new file mode 100644
index 0000000..c168318
--- /dev/null
+++ b/assets/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>status.tobyvin.dev</title>
+ <link rel="stylesheet" type="text/css" href="index.css" />
+ <script src="index.js"></script>
+ </head>
+ <body>
+ <main>
+ <header>
+ <hgroup>
+ <h1>tobyvin.dev Status</h1>
+ </hgroup>
+ </header>
+
+ <table>
+ <tr id="status">
+ <td><b>Status</b></td>
+ <td id="issues">No issues detected</td>
+ </tr>
+ </table>
+
+ <hr></hr>
+
+ <table id="services"></table>
+ </main>
+ </body>
+</html>
diff --git a/assets/index.js b/assets/index.js
new file mode 100644
index 0000000..173ed72
--- /dev/null
+++ b/assets/index.js
@@ -0,0 +1,87 @@
+/**
+ * @typedef {Object} Check
+ * @property {String} status - 'pass'|'fail'|'warn'
+ * @property {String} output - Details. Not present if 'pass'
+ */
+
+/**
+ * @typedef {Check} HealthCheck
+ * @property {Map<String, Check>} checks
+ */
+
+async function getHealthCheck() {
+ const url = "api/healthcheck";
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Response status: ${response.status}`);
+ }
+
+ const json = await response.json();
+ return json;
+ } catch (error) {
+ console.error(error.message);
+ }
+}
+
+function updateStatus(check) {
+ const statusElm = document.getElementById("status");
+ const issuesElm = document.getElementById("issues");
+ switch (check.status) {
+ case "pass":
+ issuesElm.textContent = "No issues detected";
+ statusElm.setAttribute("class", "ok");
+ break;
+ case "fail":
+ issuesElm.textContent = `${check.output} issues detected`;
+ statusElm.setAttribute("class", "error");
+ break;
+ case "warn":
+ issuesElm.textContent = `${check.output} warnings detected`;
+ statusElm.setAttribute("class", "warning");
+ break;
+ default:
+ issuesElm.textContent = "Unknown";
+ statusElm.setAttribute("class", "warning");
+ }
+}
+
+getHealthCheck().then((healthCheck) => {
+ const table = document.getElementById("services");
+ const evtSource = new EventSource("sse");
+ updateStatus(healthCheck);
+
+ for (const [service, check] of Object.entries(healthCheck.checks)) {
+ const row = table.insertRow();
+
+ const nameNode = row.insertCell();
+ nameNode.textContent = service;
+
+ const stateNode = row.insertCell();
+ switch (check.status) {
+ case "pass":
+ stateNode.textContent = "Operational";
+ stateNode.setAttribute("class", "ok");
+ break;
+ case "fail":
+ stateNode.textContent = "Down";
+ stateNode.title = check.output;
+ stateNode.setAttribute("class", "error");
+ break;
+ case "warn":
+ stateNode.textContent = "Warning";
+ stateNode.title = check.output;
+ stateNode.setAttribute("class", "warning");
+ break;
+ default:
+ stateNode.textContent = "Unknown";
+ statusElm.setAttribute("class", "warning");
+ }
+
+ evtSource.addEventListener(service, (event) => {
+ const status = JSON.parse(event.data);
+ stateNode.textContent = status.state;
+ stateNode.title = status.output;
+ });
+ }
+});