commit a944762830e71be6e279ba71b19229a1b9330c0d Author: simon987 Date: Sun May 31 13:50:21 2020 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1269488 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +data diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..5f454ea --- /dev/null +++ b/css/style.css @@ -0,0 +1,338 @@ +* { + padding: 0; + margin: 0; +} + +body { + font-family: 'Source Sans Pro', sans-serif; + font-size: 16px; +} + +header { + margin-bottom: 20px; + box-shadow: 0 2px 100px rgba(0, 0, 0, .15); + transition: background-color 2s ease; + border-bottom: 1px solid #FFF; +} + +main { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: 0 15px; +} + +#chart-grid { + display: flex; + flex-wrap: wrap; + align-content: flex-start; + width: 75%; +} + +.chart-50 { width: 50%; } +.chart-100 { width: 100%; } + +#overall-status-text { + #padding: 50px 10px 10px 10px; + color: #FFF; +} + +#overall-status.green { background-color: #40D24C; } +#overall-status.yellow { background-color: #D2C640; } +#overall-status.red { background-color: #D24040; } +#overall-status.gray { background-color: #B8B8B8; } + +#overall-status-text { + font: bold 42px 'Source Sans Pro', sans-serif; + text-transform: uppercase; + text-align: center; + line-height: 1em; +} + +.infobar { + font-size: 12px; + font-family: sans-serif; + padding: 10px; + background: rgba(255, 255, 255, .8); +} + +.infobar .item { + display: inline-block; + margin-right: 2em; +} + +#timeline { + width: 25%; + position: relative; + z-index: 1; +} + +#timeline > div { + position: relative; + z-index: 2; + margin: 2em 0; +} + +#timeline > div:first-child { + margin-top: 0; +} + +#timeline .message { + font-size: 14px; + border-width: 1px; + border-style: solid; + background: #FFF; + margin-left: 2.25em; +} + +#timeline .message-head, +#timeline .message-body { + padding: 8px; +} + +#timeline .message-head { + color: #FFF; + padding-top: 4px; + padding-bottom: 4px; + font-size: 12px; +} + +#timeline .message.green { border-color: #40D24C; } +#timeline .message.green .message-head { background-color: #40D24C; } +#timeline .message.yellow { border-color: #FFAC3B; } +#timeline .message.yellow .message-head { background-color: #FFAC3B; } +#timeline .message.red { border-color: #D24040; } +#timeline .message.red .message-head { background-color: #D24040; } + +#timeline .event { + line-height: 2em; + background-repeat: no-repeat; + background-size: auto 2em; + padding-left: 3em; +} + +#timeline .event.green { background-image: url('../images/status-green.png'); } +#timeline .event.yellow { background-image: url('../images/status-yellow.png'); } +#timeline .event.red { background-image: url('../images/status-red.png'); } +#timeline .event.gray { background-image: url('../images/status-gray.png'); } + +#timeline .event .time { + margin-right: .25em; + font-weight: bold; +} + +#timeline #bg-line { + position: absolute; + height: 100%; + border-left: 3px solid #EEE; + left: 16px; + top: 0; + margin: 0; + z-index: 0; +} + + + +#big-gap { + display: none; + color: #CC0000; + padding-left: 3em; + font-size: 14px; +} + + + + +.chart { + padding-bottom: 20px; +} + +.chart-title { + font-weight: bold; + text-align: center; +} + +.chart-title a { + color: #2D6F96; + text-decoration: none; +} + +.chart-title a:hover { + text-decoration: underline; +} + +svg { + font: 10px sans-serif; + max-width: 100%; /* thanks for nothin, Safari */ +} + +.chart-container .chart svg { + font-size: 16px; +} + +.axis path, +.axis line { + fill: none; + stroke: #CCC; + stroke-width: 1px; + shape-rendering: crispEdges; + stroke-linecap: square; +} + +.line { + fill: none; + stroke: #CCC; + stroke-width: 1px; +} + +.chart-container .chart .line { + stroke-width: 1px; +} + +.min.line, +.max.line { + stroke-dasharray: 1, 1; +} + +.chart-container .chart .min.line, +.chart-container .chart .max.line { + stroke-width: 2px; + stroke-dasharray: 5, 2; +} + +.chart-container .chart .tolerance.line { + stroke-width: 1px; + stroke-dasharray: 5, 5; +} + +.line.min { + stroke: #B1DC9C; +} + +.line.main { + stroke: #333; + stroke-linecap: round; +} + +.line.max { + stroke: #FFA447; +} + +.line.tolerance { + stroke: #FF7070; +} + +.overlay { + fill: none; + pointer-events: all; +} + +.focus circle { + fill: #333; + stroke: #333; +} + +.focus text { + font-size: 16px; + + /* this nonsense makes it look halfway decent across browsers */ + text-shadow: + 1px 1px 0 #FFF, + -1px -1px 0 #FFF, + 1px -1px 0 #FFF, + -1px 1px 0 #FFF; +} + +footer { + padding: 50px; + font-size: 12px; + color: #AAA; + font-family: sans-serif; + text-align: center; +} + +#checkup-logo { + height: 1.75em; + vertical-align: top; + margin-left: 3px; +} + + + + +@media (max-width: 1200px) { + .focus text { + font-size: 22px; + } + + #chart-grid { width: 70%; } + #timeline { width: 30%; } +} + +@media (max-width: 1050px) { + #chart-grid { width: 70%; } + #timeline { width: 30%; } +} + + +@media (max-width: 899px) { + #chart-grid { width: 60%; } + #timeline { width: 40%; } + .chart-50 { width: 100%; } + + #overall-status-text { + font-size: 34px; + padding: 15px; + } + + #timeline .message { + font-size: 16px; + } + + .infobar .item { + margin-right: 1em; + } + + .message { + font-size: 16px; + } + + .message-head, + .message-body { + padding: 16px; + } + + .message-head { + padding-top: 6px; + padding-bottom: 6px; + font-size: 14px; + } + + svg { + font-size: 20px; + } + + .axis path, + .axis line { + stroke: #DDD; + stroke-width: 2px; + } + + .line { + stroke-width: 1px; + } + + .min.line, + .max.line { + stroke-dasharray: 4, 3; + } + + .focus text { + font-size: 20px; + } +} + +@media (max-width: 600px) { + #chart-grid { width: 100%; } + #timeline { width: 100%; } +} diff --git a/images/checkup.png b/images/checkup.png new file mode 100644 index 0000000..861e254 Binary files /dev/null and b/images/checkup.png differ diff --git a/images/degraded.png b/images/degraded.png new file mode 100644 index 0000000..dd115dd Binary files /dev/null and b/images/degraded.png differ diff --git a/images/favicon.png b/images/favicon.png new file mode 100755 index 0000000..9662946 Binary files /dev/null and b/images/favicon.png differ diff --git a/images/incident.png b/images/incident.png new file mode 100644 index 0000000..8a2ba6a Binary files /dev/null and b/images/incident.png differ diff --git a/images/ok.png b/images/ok.png new file mode 100644 index 0000000..4468c2a Binary files /dev/null and b/images/ok.png differ diff --git a/images/status-gray.png b/images/status-gray.png new file mode 100755 index 0000000..fba8c8b Binary files /dev/null and b/images/status-gray.png differ diff --git a/images/status-green.png b/images/status-green.png new file mode 100755 index 0000000..7fdcd80 Binary files /dev/null and b/images/status-green.png differ diff --git a/images/status-red.png b/images/status-red.png new file mode 100755 index 0000000..f5774a5 Binary files /dev/null and b/images/status-red.png differ diff --git a/images/status-yellow.png b/images/status-yellow.png new file mode 100755 index 0000000..6f6e080 Binary files /dev/null and b/images/status-yellow.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..9dbc71f --- /dev/null +++ b/index.html @@ -0,0 +1,48 @@ + + + + Status Page + + + + + + + + + + + + +
+
+ Loading +
+
+
+ checks in the last +
+
+ Last check: +
+
+
+ +
+
+ +   +
+
+
+ There is a big gap of time where no checkups were performed, so some graphs may look distorted. +
+
+
+
+ + + + diff --git a/js/checkup.js b/js/checkup.js new file mode 100644 index 0000000..e2d0054 --- /dev/null +++ b/js/checkup.js @@ -0,0 +1,231 @@ +// checkup is the global namespace for all checkup variables (except time). +var checkup = checkup || {}; + +// time provides simple nanosecond-based unit measurements. +var time = (function() { + // now gets the current time with millisecond accuracy, + // but as a unit of nanoseconds. + var now = function() { + return new Date().getTime() * 1e6; + }; + var ns = 1, + us = 1000 * ns, + ms = 1000 * us, + second = 1000 * ms, + minute = 60 * second, + hour = 60 * minute, + day = 24 * hour, + week = 7 * day; + + return { + Now: now, + Nanosecond: ns, + Microsecond: us, + Millisecond: ms, + Second: second, + Minute: minute, + Hour: hour, + Day: day, + Week: week + }; +})(); + +// formatDuration formats d (in nanoseconds) with +// a proper unit suffix based on its value. +checkup.formatDuration = function(d) { + if (d == 0) + return d+"ms"; + else if (d < time.Millisecond) + return Math.round(d*1e-3)+"µs"; + else if (d < 10 * time.Second) + return Math.round(d*1e-6)+"ms"; + else if (d < 90 * time.Second) + return Math.round(d*1e-9)+"s"; + else if (d < 90 * time.Minute) + return Math.round(d*1e-9/60)+" minutes"; + else if (d < 48 * time.Hour) + return Math.round(d*1e-9/60/60)+" hours"; + else + return Math.round(d*1e-9 / 60/60/24)+" days"; +}; + +// I'm not even joking +checkup.leftpad = function(str, len, ch) { + str = String(str); + var i = -1; + if (!ch && ch !== 0) ch = ' '; + len = len - str.length; + while (++i < len) str = ch + str; + return str; +} + +// timeSince renders the duration ms (in milliseconds) in human-friendly form. +checkup.timeSince = function(ms) { + var seconds = Math.floor((new Date() - ms) / 1000); + var interval = Math.floor(seconds / 31536000); + if (interval > 1) return interval + " years"; + interval = Math.floor(seconds / 2592000); + if (interval > 1) return interval + " months"; + interval = Math.floor(seconds / 86400); + if (interval > 1) return interval + " days"; + interval = Math.floor(seconds / 3600); + if (interval > 1) return interval + " hours"; + interval = Math.floor(seconds / 60); + if (interval > 1) return interval + " minutes"; + return Math.floor(seconds) + " seconds"; +}; + +// makeTimeTag returns a