|
|
@ -0,0 +1,266 @@ |
|
|
|
<!DOCTYPE html> |
|
|
|
<html lang="en"> |
|
|
|
<head> |
|
|
|
<meta charset="utf-8"> |
|
|
|
<title>{{ site_name }}</title> |
|
|
|
<style> |
|
|
|
/* Colors, dark theme */ |
|
|
|
|
|
|
|
.theme-dark { |
|
|
|
background: #000; |
|
|
|
color: #FFF; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-dark #panel > div { |
|
|
|
background: #AAA; |
|
|
|
color: #000; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-dark #panel > div.status-up { |
|
|
|
color: #FFF; |
|
|
|
background: #263026; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-dark #panel > div.status-started { |
|
|
|
color: #FFF; |
|
|
|
background: #263026; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.theme-dark #panel > div.status-grace { |
|
|
|
color: #000; |
|
|
|
background: #FFB300; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-dark #panel > div.status-down { |
|
|
|
color: #000; |
|
|
|
background: #ff3929; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-dark .spinner:after { |
|
|
|
border-color: #4c604c transparent #4c604c transparent; |
|
|
|
} |
|
|
|
|
|
|
|
/* Colors, light theme */ |
|
|
|
|
|
|
|
.theme-light { |
|
|
|
background: #FFF; |
|
|
|
color: #333; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-light #panel > div { |
|
|
|
color: #000; |
|
|
|
background: #FFF; |
|
|
|
border: 2px solid #DDD; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-light #panel > div.status-grace { |
|
|
|
color: #000; |
|
|
|
background: #FFAB00; |
|
|
|
border: none; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-light #panel > div.status-down { |
|
|
|
color: #FFF; |
|
|
|
background: #D81818; |
|
|
|
border: none; |
|
|
|
} |
|
|
|
|
|
|
|
.theme-light .spinner:after { |
|
|
|
border-color: #DDD transparent #DDD transparent; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Spinner from https://loading.io/css/ */ |
|
|
|
.spinner { |
|
|
|
display: none; |
|
|
|
} |
|
|
|
|
|
|
|
div.status-started .spinner { |
|
|
|
position: absolute; |
|
|
|
right: 8px; |
|
|
|
top: 50%; |
|
|
|
margin-top: -18px; |
|
|
|
display: inline-block; |
|
|
|
width: 36px; |
|
|
|
height: 36px; |
|
|
|
} |
|
|
|
.spinner:after { |
|
|
|
content: " "; |
|
|
|
display: block; |
|
|
|
width: 24px; |
|
|
|
height: 24px; |
|
|
|
margin: 1px; |
|
|
|
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(200px, 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 { |
|
|
|
position: absolute; |
|
|
|
font-size: 13px; |
|
|
|
opacity: 0.8; |
|
|
|
} |
|
|
|
|
|
|
|
#panel > div .lp { |
|
|
|
bottom: 8px; |
|
|
|
left: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
.check-template { |
|
|
|
display: none; |
|
|
|
} |
|
|
|
</style> |
|
|
|
</head> |
|
|
|
<body class="theme-light"> |
|
|
|
<div id="panel"></div> |
|
|
|
|
|
|
|
<div class="check-template"> |
|
|
|
<div class="name"></div> |
|
|
|
<div class="lp"></div> |
|
|
|
<div class="spinner"></div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<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", "/api/v1/checks/"); |
|
|
|
httpRequest.setRequestHeader("X-Api-Key", key); |
|
|
|
httpRequest.send(); |
|
|
|
} |
|
|
|
|
|
|
|
function timeSince(date) { |
|
|
|
var v = Math.floor((new Date() - date) / 1000); |
|
|
|
|
|
|
|
if (v < 60) { // v is seconds |
|
|
|
return v + " second" + (v == 1 ? "" : "s"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
v = Math.floor(v / 60); // v is now minutes |
|
|
|
if (v < 60) { |
|
|
|
return v + " minute" + (v == 1 ? "" : "s"); |
|
|
|
} |
|
|
|
|
|
|
|
v = Math.floor(v / 60); // v is now hours |
|
|
|
if (v < 24) { |
|
|
|
return v + " hour" + (v == 1 ? "" : "s"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
v = Math.floor(v / 24); // v is now days |
|
|
|
return v + " day" + (v == 1 ? "" : "s"); |
|
|
|
}; |
|
|
|
|
|
|
|
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); |
|
|
|
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; |
|
|
|
} |
|
|
|
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> |