You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
7.1 KiB

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>{{ site_name }}</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <style>
  8. /* Colors, dark theme */
  9. .theme-dark {
  10. background: #000;
  11. color: #FFF;
  12. }
  13. .theme-dark #panel > div {
  14. background: #AAA;
  15. color: #000;
  16. }
  17. .theme-dark #panel > div.status-up {
  18. color: #FFF;
  19. background: #263026;
  20. }
  21. .theme-dark #panel > div.status-started {
  22. color: #FFF;
  23. background: #263026;
  24. }
  25. .theme-dark #panel > div.status-grace {
  26. color: #000;
  27. background: #FFB300;
  28. }
  29. .theme-dark #panel > div.status-down {
  30. color: #000;
  31. background: #ff3929;
  32. }
  33. .theme-dark .spinner:after {
  34. border-color: #4c604c transparent #4c604c transparent;
  35. }
  36. /* Colors, light theme */
  37. .theme-light {
  38. background: #FFF;
  39. color: #333;
  40. }
  41. .theme-light #panel > div {
  42. color: #000;
  43. background: #FFF;
  44. border: 2px solid #DDD;
  45. }
  46. .theme-light #panel > div.status-grace {
  47. color: #000;
  48. background: #FFAB00;
  49. border: none;
  50. }
  51. .theme-light #panel > div.status-down {
  52. color: #FFF;
  53. background: #D81818;
  54. border: none;
  55. }
  56. .theme-light .spinner:after {
  57. border-color: #DDD transparent #DDD transparent;
  58. }
  59. /* Spinner from https://loading.io/css/ */
  60. .spinner {
  61. display: none;
  62. }
  63. div.status-started .spinner {
  64. position: absolute;
  65. right: 8px;
  66. top: 50%;
  67. margin-top: -18px;
  68. display: inline-block;
  69. width: 36px;
  70. height: 36px;
  71. }
  72. .spinner:after {
  73. content: " ";
  74. display: block;
  75. width: 24px;
  76. height: 24px;
  77. margin: 1px;
  78. border-radius: 50%;
  79. border: 5px solid transparent;
  80. animation: lds-dual-ring 1.25s linear infinite;
  81. }
  82. @keyframes lds-dual-ring {
  83. 0% {
  84. transform: rotate(0deg);
  85. }
  86. 100% {
  87. transform: rotate(360deg);
  88. }
  89. }
  90. /* Layout and font */
  91. html, body {
  92. font-family: -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;
  93. height: 100%;
  94. margin: 0;
  95. }
  96. #panel {
  97. display: grid;
  98. grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  99. grid-gap: 8px;
  100. padding: 8px;
  101. }
  102. #panel > h1 {
  103. grid-column-start: 1;
  104. grid-column-end: -1;
  105. margin: 12px 0;
  106. font-size: 20px;
  107. }
  108. #panel > div {
  109. padding: 8px 8px 32px 8px;
  110. font-size: 18px;
  111. position: relative;
  112. }
  113. #panel > div .name {
  114. text-overflow: ellipsis;
  115. overflow: hidden;
  116. }
  117. #panel > div .lp {
  118. position: absolute;
  119. font-size: 13px;
  120. opacity: 0.8;
  121. }
  122. #panel > div .lp {
  123. bottom: 8px;
  124. left: 8px;
  125. }
  126. .check-template {
  127. display: none;
  128. }
  129. </style>
  130. </head>
  131. <body class="theme-light">
  132. <div id="panel"></div>
  133. <div class="check-template">
  134. <div class="name"></div>
  135. <div class="lp"></div>
  136. <div class="spinner"></div>
  137. </div>
  138. <script>
  139. function fetch(key, callback) {
  140. var httpRequest = new XMLHttpRequest();
  141. httpRequest.onreadystatechange = function() {
  142. if (httpRequest.readyState === 4) {
  143. if (httpRequest.status === 200) {
  144. callback(JSON.parse(httpRequest.responseText));
  145. }
  146. }
  147. };
  148. httpRequest.open("GET", "/api/v1/checks/");
  149. httpRequest.setRequestHeader("X-Api-Key", key);
  150. httpRequest.send();
  151. }
  152. function timeSince(date) {
  153. var v = Math.floor((new Date() - date) / 1000);
  154. if (v < 60) { // v is seconds
  155. return v + " second" + (v == 1 ? "" : "s");
  156. }
  157. v = Math.floor(v / 60); // v is now minutes
  158. if (v < 60) {
  159. return v + " minute" + (v == 1 ? "" : "s");
  160. }
  161. v = Math.floor(v / 60); // v is now hours
  162. if (v < 24) {
  163. return v + " hour" + (v == 1 ? "" : "s");
  164. }
  165. v = Math.floor(v / 24); // v is now days
  166. return v + " day" + (v == 1 ? "" : "s");
  167. };
  168. var template = document.querySelector(".check-template");
  169. function updatePanel(node) {
  170. fetch(node.dataset.readonlyKey, function(doc) {
  171. var tag = "TAG_" + node.dataset.readonlyKey.substr(0, 6);
  172. // Sort returned checks by name:
  173. var sorted = doc.checks.sort(function(a, b) {
  174. return a.name.localeCompare(b.name)
  175. });
  176. var fragment = document.createDocumentFragment();
  177. sorted.forEach(function(item) {
  178. var div = template.cloneNode(true);
  179. div.setAttribute("class", tag + " status-" + item.status);
  180. div.querySelector(".name").textContent = item.name || "unnamed";
  181. if (item.last_ping) {
  182. var s = timeSince(Date.parse(item.last_ping)) + " ago";
  183. div.querySelector(".lp").textContent = s;
  184. }
  185. fragment.appendChild(div);
  186. });
  187. document.querySelectorAll('.' + tag).forEach(function(element) {
  188. element.remove();
  189. });
  190. node.parentNode.insertBefore(fragment, node.nextSibling);
  191. });
  192. }
  193. if (window.location.hash) {
  194. var panel;
  195. var pairs = window.location.hash.substr(1).split("&");
  196. for (var i=0, pair; pair=pairs[i]; i++) {
  197. if (pair.indexOf("theme=") != -1) {
  198. document.body.setAttribute("class", pair.replace("=", "-"));
  199. continue;
  200. }
  201. var parts = pair.split("=");
  202. var h1 = document.createElement("H1");
  203. h1.dataset.readonlyKey = parts[0];
  204. if (parts[1]) {
  205. h1.innerText = decodeURIComponent(parts[1]);
  206. }
  207. if (!panel) {
  208. panel = document.getElementById("panel");
  209. panel.innerHTML = "";
  210. }
  211. panel.appendChild(h1);
  212. }
  213. }
  214. document.querySelectorAll("h1").forEach(updatePanel);
  215. setInterval(function() {
  216. document.querySelectorAll("h1").forEach(updatePanel);
  217. }, 5000);
  218. </script>
  219. </body>
  220. </html>