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.

266 lines
7.1 KiB

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