mirror of
https://github.com/healthchecks/dashboard.git
synced 2026-02-17 15:47:39 +01:00
307 lines
8.7 KiB
HTML
307 lines
8.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Healthchecks.io</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<style>
|
|
/* Colors, dark theme */
|
|
|
|
.theme-dark {
|
|
background: #000;
|
|
color: #FFF;
|
|
fill: #FFF;
|
|
}
|
|
|
|
.theme-dark #panel > div {
|
|
background: #AAA;
|
|
color: #000;
|
|
fill: #000;
|
|
}
|
|
|
|
.theme-dark #panel > div.status-up {
|
|
color: #FFF;
|
|
fill: #FFF;
|
|
background: #263026;
|
|
}
|
|
|
|
.theme-dark #panel > div.status-grace {
|
|
color: #000;
|
|
fill: #000;
|
|
background: #FFB300;
|
|
}
|
|
|
|
.theme-dark #panel > div.status-down {
|
|
color: #000;
|
|
fill: #000;
|
|
background: #ff3929;
|
|
}
|
|
|
|
.theme-dark .spinner:after {
|
|
border-color: rgba(255, 255, 255, 0.3) transparent rgba(255, 255, 255, 0.3) transparent;
|
|
}
|
|
|
|
/* Colors, light theme */
|
|
|
|
.theme-light {
|
|
background: #FFF;
|
|
color: #333;
|
|
fill: #333;
|
|
}
|
|
|
|
.theme-light #panel > div {
|
|
color: #000;
|
|
fill: #000;
|
|
background: #FFF;
|
|
border: 2px solid #DDD;
|
|
}
|
|
|
|
.theme-light #panel > div.status-grace {
|
|
color: #000;
|
|
fill: #000;
|
|
background: #FFAB00;
|
|
border: none;
|
|
}
|
|
|
|
.theme-light #panel > div.status-down {
|
|
color: #FFF;
|
|
fill: #FFF;
|
|
background: #D81818;
|
|
border: none;
|
|
}
|
|
|
|
.theme-light .spinner:after {
|
|
border-color: rgba(0, 0, 0, 0.2) transparent rgba(0, 0, 0, 0.2) transparent;
|
|
}
|
|
|
|
/* Spinner from https://loading.io/css/ */
|
|
.spinner {
|
|
display: none;
|
|
}
|
|
|
|
div.status-started .spinner {
|
|
float: right;
|
|
display: inline-block;
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
.spinner:after {
|
|
content: " ";
|
|
display: block;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
border: 5px solid transparent;
|
|
animation: lds-dual-ring 1.25s linear infinite;
|
|
}
|
|
|
|
@keyframes lds-dual-ring {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* Layout and font */
|
|
|
|
html, body {
|
|
font-family: -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;
|
|
height: 100%;
|
|
margin: 0;
|
|
}
|
|
|
|
#panel {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
grid-gap: 8px;
|
|
padding: 8px;
|
|
}
|
|
|
|
#panel > h1 {
|
|
grid-column-start: 1;
|
|
grid-column-end: -1;
|
|
margin: 12px 0;
|
|
font-size: 20px;
|
|
}
|
|
|
|
#panel > div {
|
|
padding: 8px 8px 32px 8px;
|
|
font-size: 18px;
|
|
position: relative;
|
|
}
|
|
|
|
#panel > div .name {
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#panel > div .lp,
|
|
#panel > div .ld {
|
|
position: absolute;
|
|
font-size: 13px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
#panel > div .lp {
|
|
bottom: 8px;
|
|
left: 8px;
|
|
}
|
|
|
|
#panel > div .ld {
|
|
bottom: 8px;
|
|
right: 8px;
|
|
}
|
|
|
|
#panel > div .ld > svg {
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
|
|
|
|
.check-template {
|
|
display: none;
|
|
}
|
|
|
|
body > svg {
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="theme-light">
|
|
<div id="panel">
|
|
<h1 data-readonly-key="uKatH7z6dSuN2Zyf1luRCmPDkw3fw2U0">Demo Project</h1>
|
|
<h1 data-readonly-key="UKsc30GIblRMKKN4BEPXcBNLa8bx4grU">Uptime Checkers</h1>
|
|
</div>
|
|
|
|
<div class="check-template">
|
|
<div class="spinner"></div>
|
|
<div class="name"></div>
|
|
<div class="lp"></div>
|
|
<div class="ld">
|
|
<svg width="13" height="13" viewBox="0 0 768 768">
|
|
<use href="#timer" />
|
|
</svg>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<svg>
|
|
<defs>
|
|
<symbol id="timer">
|
|
<path d="M384 640.5c124.5 0 223.5-100.5 223.5-225s-99-223.5-223.5-223.5-223.5 99-223.5 223.5 99 225 223.5 225zM609 237c39 49.5 63 111 63 178.5 0 159-129 288-288 288s-288-129-288-288 129-288 288-288c67.5 0 130.5 25.5 180 64.5l45-46.5c16.5 13.5 31.5 28.5 45 45zM352.5 448.5v-192h63v192h-63zM480 31.5v64.5h-192v-64.5h192z"></path>
|
|
</symbol>
|
|
</defs>
|
|
</svg>
|
|
|
|
<script>
|
|
function fetch(key, callback) {
|
|
var httpRequest = new XMLHttpRequest();
|
|
httpRequest.onreadystatechange = function() {
|
|
if (httpRequest.readyState === 4) {
|
|
if (httpRequest.status === 200) {
|
|
callback(JSON.parse(httpRequest.responseText));
|
|
}
|
|
}
|
|
};
|
|
httpRequest.open("GET", "https://healthchecks.io/api/v3/checks/");
|
|
httpRequest.setRequestHeader("X-Api-Key", key);
|
|
httpRequest.send();
|
|
}
|
|
|
|
function duration(v) {
|
|
if (v < 60) { // v is seconds
|
|
return v + " sec";
|
|
}
|
|
|
|
v = Math.floor(v / 60); // v is now minutes
|
|
if (v < 60) {
|
|
return v + " min";
|
|
}
|
|
|
|
v = Math.floor(v / 60); // v is now hours
|
|
if (v < 24) {
|
|
return v + " h";
|
|
}
|
|
|
|
v = Math.floor(v / 24); // v is now days
|
|
return v + " day" + (v == 1 ? "" : "s");
|
|
};
|
|
|
|
function timeSince(date) {
|
|
var v = Math.floor((new Date() - date) / 1000);
|
|
return duration(v);
|
|
};
|
|
|
|
var template = document.querySelector(".check-template");
|
|
function updatePanel(node) {
|
|
fetch(node.dataset.readonlyKey, function(doc) {
|
|
var tag = "TAG_" + node.dataset.readonlyKey.substr(0, 6);
|
|
|
|
// Sort returned checks by name:
|
|
var sorted = doc.checks.sort(function(a, b) {
|
|
return a.name.localeCompare(b.name)
|
|
});
|
|
|
|
var fragment = document.createDocumentFragment();
|
|
sorted.forEach(function(item) {
|
|
var div = template.cloneNode(true);
|
|
div.setAttribute("class", tag + " status-" + item.status + (item.started ? " status-started" : ""));
|
|
div.querySelector(".name").textContent = item.name || "unnamed";
|
|
if (item.last_ping) {
|
|
var s = timeSince(Date.parse(item.last_ping)) + " ago";
|
|
div.querySelector(".lp").textContent = s;
|
|
}
|
|
if (item.last_duration) {
|
|
var svg = '<svg width="13" height="13" viewBox="0 0 768 768"><use href="#timer" /></svg> ';
|
|
div.querySelector(".ld span").textContent = duration(item.last_duration);
|
|
} else {
|
|
div.querySelector(".ld").textContent = "";
|
|
}
|
|
fragment.appendChild(div);
|
|
});
|
|
|
|
|
|
document.querySelectorAll('.' + tag).forEach(function(element) {
|
|
element.remove();
|
|
});
|
|
|
|
node.parentNode.insertBefore(fragment, node.nextSibling);
|
|
});
|
|
}
|
|
|
|
if (window.location.hash) {
|
|
var panel;
|
|
|
|
var pairs = window.location.hash.substr(1).split("&");
|
|
for (var i=0, pair; pair=pairs[i]; i++) {
|
|
if (pair.indexOf("theme=") != -1) {
|
|
document.body.setAttribute("class", pair.replace("=", "-"));
|
|
continue;
|
|
}
|
|
|
|
var parts = pair.split("=");
|
|
var h1 = document.createElement("H1");
|
|
h1.dataset.readonlyKey = parts[0];
|
|
if (parts[1]) {
|
|
h1.innerText = decodeURIComponent(parts[1]);
|
|
}
|
|
|
|
if (!panel) {
|
|
panel = document.getElementById("panel");
|
|
panel.innerHTML = "";
|
|
}
|
|
panel.appendChild(h1);
|
|
}
|
|
}
|
|
document.querySelectorAll("h1").forEach(updatePanel);
|
|
setInterval(function() {
|
|
document.querySelectorAll("h1").forEach(updatePanel);
|
|
}, 5000);
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|