uptime.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <div class="standardContainer">
  2. <div class="ui basic segment">
  3. <h2>Uptime Monitor</h2>
  4. <p>Check the online state of proxied targets</p>
  5. </div>
  6. <div class="ui divider"></div>
  7. <div id="utmrender" class="ui basic segment">
  8. <div class="ui basic segment">
  9. <h4 class="ui header">
  10. <i class="red remove icon"></i>
  11. <div class="content">
  12. Uptime Monitoring service is currently unavailable
  13. <div class="sub header">This might be caused by an error in cluster communication within the host servers. Please wait for administrator to resolve the issue.</div>
  14. </div>
  15. </h4>
  16. </div>
  17. </div>
  18. <div align="center">
  19. <button class="ui basic circular green icon button" onclick="reloadUptimeList();"><i class="refresh icon"></i></button>
  20. </div>
  21. </div>
  22. <script>
  23. var uptime5xxErrorMessage = {
  24. "500": "Internal Server Error",
  25. "501": "Not Implemented",
  26. "502": "Bad Gateway",
  27. "503": "Service Unavailable",
  28. "504": "Gateway Timeout",
  29. "505": "HTTP Version Not Supported",
  30. "506": "Variant Also Negotiates",
  31. "507": "Insufficient Storage",
  32. "508": "Loop Detected",
  33. "510": "Not Extended",
  34. "511": "Network Authentication Required",
  35. "520": "Web Server Returned an Unknown Error (Cloudflare)",
  36. "521": "Web Server is Down (Cloudflare)",
  37. "522": "Connection Timed Out (Cloudflare)",
  38. "523": "Origin is Unreachable (Cloudflare)",
  39. "524": "A Timeout Occurred (Cloudflare)",
  40. "525": "SSL Handshake Failed (Cloudflare)",
  41. "526": "Invalid SSL Certificate (Cloudflare)",
  42. "527": "Railgun Error (Cloudflare)",
  43. "530": "Site is Frozen (Pantheon)"
  44. }
  45. $('#utmEnable').checkbox({
  46. onChange: function() {
  47. var utmEnable = $('input[name="utmEnable"]').is(":checked");
  48. $.post({
  49. url: '/api/toggle-utm',
  50. data: {utmEnable: utmEnable},
  51. success: function(response) {
  52. console.log(response);
  53. },
  54. error: function(error) {
  55. console.log(error);
  56. }
  57. });
  58. }
  59. });
  60. function initUptimeTable(){
  61. $.get("/api/utm/list", function(data){
  62. let records = data;
  63. renderRecords(records);
  64. })
  65. }
  66. initUptimeTable();
  67. function reloadUptimeList(){
  68. $("#utmrender").html(`<div class="ui utmloading segment">
  69. <div class="ui active inverted dimmer" style="z-index: 2;">
  70. <div class="ui text loader">Loading</div>
  71. </div>
  72. <br><br><br><br>
  73. </div>`);
  74. setTimeout(initUptimeTable, 300);
  75. }
  76. //For every 5 minutes
  77. setInterval(function(){
  78. $.get("/api/utm/list", function(data){
  79. console.log("Status Updated");
  80. records = data;
  81. renderRecords(records);
  82. });
  83. }, (300 * 1000));
  84. function renderRecords(records){
  85. $("#utmrender").html("");
  86. for (let [key, value] of Object.entries(records)) {
  87. renderUptimeData(key, value);
  88. }
  89. }
  90. function format_time(s) {
  91. const date = new Date(s * 1e3);
  92. return(date.toLocaleString());
  93. }
  94. function resolveUptime5xxErrorMessage(errorCode){
  95. if (uptime5xxErrorMessage[errorCode] != undefined){
  96. return uptime5xxErrorMessage[errorCode]
  97. }else{
  98. return "Unknown Error";
  99. }
  100. }
  101. function showStatusDotInfo(targetDot){
  102. $(".statusbar .selectedDotInfo").hide();
  103. let payload = $(targetDot).attr("payload");
  104. let statusData = JSON.parse(decodeURIComponent(payload));
  105. let statusDotInfoEle = $(targetDot).parent().parent().find(".selectedDotInfo");
  106. let statusInfoEle = $(statusDotInfoEle).find(".status_dot_status_info");
  107. //Fill in the data to the info box
  108. $(statusDotInfoEle).find(".status_dot_timestamp").text(format_time(statusData.Timestamp));
  109. $(statusDotInfoEle).find(".status_dot_latency").text(statusData.Latency + "ms");
  110. $(statusDotInfoEle).find(".status_dot_status_code").text(statusData.StatusCode);
  111. //Set the class of the info box if status code is 5xx
  112. $(statusDotInfoEle).removeClass("yellow");
  113. $(statusDotInfoEle).removeClass("red");
  114. $(statusDotInfoEle).removeClass("green");
  115. if (statusData.StatusCode >= 500 && statusData.StatusCode < 600){
  116. $(statusDotInfoEle).addClass("yellow");
  117. $(statusInfoEle).text(httpErrorStatusCodeToText(statusData.StatusCode));
  118. }else if (statusData.StatusCode == 0 && !statusData.Online){
  119. $(statusDotInfoEle).addClass("red");
  120. $(statusInfoEle).text("Upstream is offline");
  121. }else{
  122. $(statusDotInfoEle).addClass("green");
  123. $(statusInfoEle).text("Upstream Online");
  124. }
  125. $(statusDotInfoEle).show();
  126. }
  127. function renderUptimeData(key, value){
  128. if (value.length == 0){
  129. return
  130. }
  131. let id = value[0].ID;
  132. let name = value[0].Name;
  133. let url = value[value.length - 1].URL;
  134. let protocol = value[0].Protocol;
  135. //Generate the status dot
  136. let statusDotList = ``;
  137. for(var i = 0; i < (288 - value.length); i++){
  138. //Padding
  139. statusDotList += `<div class="padding statusDot"></div>`
  140. }
  141. let ontimeRate = 0;
  142. for (var i = 0; i < value.length; i++){
  143. //Render status to html
  144. let thisStatus = value[i];
  145. let dotType = "";
  146. let statusCode = thisStatus.StatusCode;
  147. let statusDotPayload = encodeURIComponent(JSON.stringify(thisStatus));
  148. if (!thisStatus.Online && statusCode == 0){
  149. dotType = "offline";
  150. }else if (statusCode < 200){
  151. //1xx
  152. dotType = "error";
  153. ontimeRate++;
  154. }else if (statusCode < 300){
  155. //2xx
  156. dotType = "online";
  157. ontimeRate++;
  158. }else if (statusCode < 400){
  159. //3xx
  160. dotType = "online";
  161. ontimeRate++;
  162. }else if (statusCode < 500){
  163. //4xx
  164. dotType = "error";
  165. ontimeRate++;
  166. }else if (statusCode < 600){
  167. //5xx
  168. dotType = "error";
  169. }else {
  170. dotType = "offline";
  171. }
  172. let datetime = format_time(thisStatus.Timestamp);
  173. statusDotList += `<div title="${datetime}" class="${dotType} statusDot" payload="${statusDotPayload}" onclick="showStatusDotInfo(this);"></div>`
  174. }
  175. ontimeRate = ontimeRate / value.length * 100;
  176. let ontimeColor = "#df484a"
  177. if (ontimeRate > 0.8){
  178. ontimeColor = "#3bd671";
  179. }else if(ontimeRate > 0.5) {
  180. ontimeColor = "#f29030";
  181. }
  182. //Check of online status now
  183. let currentOnlineStatus = "Unknown";
  184. let onlineStatusCss = ``;
  185. let reminderEle = ``;
  186. if (value[value.length - 1].Online){
  187. currentOnlineStatus = `<i class="circle icon"></i> Online`;
  188. onlineStatusCss = `color: #3bd671;`;
  189. }else{
  190. if (value[value.length - 1].StatusCode >= 500 && value[value.length - 1].StatusCode < 600){
  191. var latestStatusCode = value[value.length - 1].StatusCode
  192. currentOnlineStatus = `<i class="exclamation circle icon"></i>${latestStatusCode} - ${resolveUptime5xxErrorMessage(latestStatusCode)}`;
  193. onlineStatusCss = `color: #f38020;`;
  194. reminderEle = `<small style="${onlineStatusCss}">Downstream proxy server is responsive but returning server error</small>`;
  195. }else if (value[value.length - 1].StatusCode >= 400 && value[value.length - 1].StatusCode <= 405){
  196. let latestStatusCode = value[value.length - 1].StatusCode;
  197. switch(latestStatusCode){
  198. case 400:
  199. currentOnlineStatus = `<i class="exclamation circle icon"></i> Bad Request`;
  200. break;
  201. case 401:
  202. currentOnlineStatus = `<i class="exclamation circle icon"></i> Unauthorized`;
  203. break;
  204. case 403:
  205. currentOnlineStatus = `<i class="exclamation circle icon"></i> Forbidden`;
  206. break;
  207. case 404:
  208. currentOnlineStatus = `<i class="exclamation circle icon"></i> Not Found`;
  209. break;
  210. case 405:
  211. currentOnlineStatus = `<i class="exclamation circle icon"></i> Method Not Allowed`;
  212. break;
  213. default:
  214. currentOnlineStatus = `<i class="exclamation circle icon"></i> Status Code: ${latestStatusCode}`;
  215. break;
  216. }
  217. onlineStatusCss = `color: #f38020;`;
  218. reminderEle = `<small style="${onlineStatusCss}">Target online but not accessible</small>`;
  219. }else{
  220. currentOnlineStatus = `<i class="circle icon"></i> Offline`;
  221. onlineStatusCss = `color: #df484a;`;
  222. }
  223. }
  224. //Generate the html
  225. $("#utmrender").append(`<div class="ui basic segment statusbar">
  226. <div class="domain">
  227. <div style="position: absolute; top: 0; right: 0.4em;">
  228. <p class="onlineStatus" style="display: inline-block; font-size: 1.2em; padding-right: 0.5em; padding-left: 0.3em; ${onlineStatusCss}">${currentOnlineStatus}</p>
  229. </div>
  230. <div>
  231. <h3 class="ui header" style="margin-bottom: 0.2em;">${name}</h3>
  232. <a href="${url}" target="_blank">${url}</a> | <span style="color: ${ontimeColor};">${(ontimeRate).toFixed(2)}%<span>
  233. </div>
  234. <div class="ui basic label protocol" style="position: absolute; bottom: 0; right: 0.2em; margin-bottom: -0.6em;">
  235. proto: ${protocol}
  236. </div>
  237. </div>
  238. <div class="status" style="marign-top: 1em;">
  239. ${statusDotList}
  240. </div>
  241. ${reminderEle}
  242. <div class="ui basic segment selectedDotInfo" style="display:none; border: 0.4em;">
  243. <div class="ui list">
  244. <div class="item"><b>Timestamp</b>: <span class="status_dot_timestamp"></span></div>
  245. <div class="item"><b>Latency</b>: <span class="status_dot_latency"></span></div>
  246. <div class="item"><b>Status Code</b>: <span class="status_dot_status_code"></span></div>
  247. <div class="item"><b>Status Info</b>: <span class="status_dot_status_info"></span></div>
  248. </div>
  249. <button onclick="$(this).parent().hide();" style="position: absolute; right: 0.4em; top: 0.6em;" class="ui basic tiny circular icon button"><i class="ui times icon"></i></button>
  250. </div>
  251. <div class="ui divider"></div>
  252. </div>`);
  253. }
  254. function httpErrorStatusCodeToText(statusCode){
  255. switch(statusCode){
  256. case 400:
  257. return "Bad Request";
  258. case 401:
  259. return "Unauthorized";
  260. case 403:
  261. return "Forbidden";
  262. case 404:
  263. return "Not Found";
  264. case 405:
  265. return "Method Not Allowed";
  266. case 500:
  267. return "Internal Server Error";
  268. case 501:
  269. return "Not Implemented";
  270. case 502:
  271. return "Bad Gateway";
  272. case 503:
  273. return "Service Unavailable";
  274. case 504:
  275. return "Gateway Timeout";
  276. case 505:
  277. return "HTTP Version Not Supported";
  278. case 506:
  279. return "Variant Also Negotiates";
  280. case 507:
  281. return "Insufficient Storage";
  282. case 508:
  283. return "Loop Detected";
  284. case 510:
  285. return "Not Extended";
  286. case 511:
  287. return "Network Authentication Required";
  288. case 520:
  289. return "Web Server Returned an Unknown Error (Cloudflare)";
  290. case 521:
  291. return "Web Server is Down (Cloudflare)";
  292. case 522:
  293. return "Connection Timed Out (Cloudflare)";
  294. case 523:
  295. return "Origin is Unreachable (Cloudflare)";
  296. case 524:
  297. return "A Timeout Occurred (Cloudflare)";
  298. case 525:
  299. return "SSL Handshake Failed (Cloudflare)";
  300. case 526:
  301. return "Invalid SSL Certificate (Cloudflare)";
  302. case 527:
  303. return "Railgun Error (Cloudflare)";
  304. default:
  305. return "Unknown Error";
  306. }
  307. }
  308. </script>