diff options
Diffstat (limited to 'assets')
-rw-r--r-- | assets/index.css | 61 | ||||
-rw-r--r-- | assets/index.html | 30 | ||||
-rw-r--r-- | assets/index.js | 87 |
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; + }); + } +}); |