upstreams.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <!-- Notes: This should be open in its original path-->
  5. <meta charset="utf-8">
  6. <link rel="stylesheet" href="../script/semantic/semantic.min.css">
  7. <script src="../script/jquery-3.6.0.min.js"></script>
  8. <script src="../script/semantic/semantic.min.js"></script>
  9. <style>
  10. .upstreamActions{
  11. position: absolute;
  12. top: 0.6em;
  13. right: 0.6em;
  14. }
  15. .upstreamLink{
  16. max-width: 220px;
  17. display: inline-block;
  18. word-break: break-all;
  19. }
  20. .ui.toggle.checkbox input:checked ~ label::before{
  21. background-color: #00ca52 !important;
  22. }
  23. #upstreamTable{
  24. max-height: 480px;
  25. border-radius: 0.3em;
  26. padding: 0.3em;
  27. overflow-y: auto;
  28. }
  29. @media (max-width: 499px) {
  30. .upstreamActions{
  31. position: relative;
  32. margin-top: 1em;
  33. margin-left: 0.4em;
  34. margin-bottom: 0.4em;
  35. }
  36. }
  37. </style>
  38. </head>
  39. <body>
  40. <br>
  41. <div class="ui container">
  42. <div class="ui header">
  43. <div class="content">
  44. Upstreams / Load Balance
  45. <div class="sub header epname"></div>
  46. </div>
  47. </div>
  48. <div class="ui divider"></div>
  49. <div class="ui small pointing secondary menu">
  50. <a class="item active narrowpadding" data-tab="upstreamlist">Upstreams</a>
  51. <a class="item narrowpadding" data-tab="newupstream">Add Upstream</a>
  52. </div>
  53. <div class="ui tab basic segment active" data-tab="upstreamlist">
  54. <!-- A list of current existing upstream on this reverse proxy-->
  55. <div id="upstreamTable">
  56. <div class="ui segment">
  57. <a><i class="ui loading spinner icon"></i> Loading</a>
  58. </div>
  59. </div>
  60. <div class="ui message">
  61. <i class="ui blue info circle icon"></i> Round-robin load balancing algorithm will be used for upstreams with same priority. Lower priority origin server will be used as fallback when all the higher priority server are offline.
  62. </div>
  63. </div>
  64. <div class="ui tab basic segment" data-tab="newupstream">
  65. <!-- Web Form to create a new upstream -->
  66. <h4 class="ui header">
  67. <i class="green add icon"></i>
  68. <div class="content">
  69. Add Upstream Server
  70. <div class="sub header">Create new load balance or fallback upstream origin</div>
  71. </div>
  72. </h4>
  73. <p style="margin-bottom: 0.4em;">Target IP address with port</p>
  74. <div class="ui fluid small input">
  75. <input type="text" id="originURL" onchange="cleanProxyTargetValue(this);"><br>
  76. </div>
  77. <small>E.g. 192.168.0.101:8000 or example.com</small>
  78. <br><br>
  79. <div class="ui checkbox">
  80. <input type="checkbox" id="requireTLS">
  81. <label>Require TLS<br>
  82. <small>Proxy target require HTTPS connection</small></label>
  83. </div><br>
  84. <div class="ui checkbox" style="margin-top: 0.6em;">
  85. <input type="checkbox" id="skipTlsVerification">
  86. <label>Skip Verification<br>
  87. <small>Check this if proxy target is using self signed certificates</small></label>
  88. </div><br>
  89. <div class="ui checkbox" style="margin-top: 0.4em;">
  90. <input type="checkbox" id="SkipWebSocketOriginCheck" checked>
  91. <label>Skip WebSocket Origin Check<br>
  92. <small>Check this to allow cross-origin websocket requests</small></label>
  93. </div>
  94. <br><br>
  95. <button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
  96. </div>
  97. <div class="ui divider"></div>
  98. <div class="field" >
  99. <button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
  100. </div>
  101. </div>
  102. <br><br><br><br>
  103. </div>
  104. <script>
  105. let origins = [];
  106. let editingEndpoint = {};
  107. $('.menu .item').tab();
  108. function initOriginList(){
  109. $.ajax({
  110. url: "/api/proxy/detail",
  111. method: "POST",
  112. data: {
  113. "type":"host",
  114. "epname": editingEndpoint.ep
  115. },
  116. success: function(data){
  117. if (data.error != undefined){
  118. //This endpoint not exists?
  119. alert(data.error);
  120. return;
  121. }else{
  122. $("#upstreamTable").html("");
  123. if (data.Origins != undefined){
  124. origins = data.Origins;
  125. console.log(origins);
  126. data.Origins.forEach(upstream => {
  127. let tlsIcon = "";
  128. if (upstream.RequireTLS){
  129. tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
  130. if (upstream.SkipCertValidations){
  131. tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
  132. }
  133. }
  134. //Priority Arrows
  135. let upArrowClass = "";
  136. if (upstream.Priority == 0 ){
  137. //Cannot go any higher
  138. upArrowClass = "disabled";
  139. }
  140. let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
  141. $("#upstreamTable").append(`<div class="ui segment">
  142. <h4 class="ui header">
  143. <div class="ui toggle checkbox" style="display:inline-block;">
  144. <input type="checkbox" name="enabled" style="margin-top: 0.4em;" ${!upstream.Disabled?"checked":""}>
  145. <label></label>
  146. </div>
  147. <div class="content">
  148. <a href="${url}" target="_blank" class="upstreamLink">${upstream.OriginIpOrDomain} ${tlsIcon}</a>
  149. <div class="sub header">Online | Priority: ${upstream.Priority}</div>
  150. </div>
  151. </h4>
  152. <div class="ui divider"></div>
  153. <div class="ui checkbox">
  154. <input type="checkbox" name="example">
  155. <label>Require TLS<br>
  156. <small>Proxy target require HTTPS connection</small></label>
  157. </div><br>
  158. <div class="ui checkbox" style="margin-top: 0.6em;">
  159. <input type="checkbox" name="example">
  160. <label>Skip Verification<br>
  161. <small>Check this if proxy target is using self signed certificates</small></label>
  162. </div><br>
  163. <div class="ui checkbox" style="margin-top: 0.4em;">
  164. <input type="checkbox" class="SkipWebSocketOriginCheck" ${upstream.SkipWebSocketOriginCheck?"checked":""}>
  165. <label>Skip WebSocket Origin Check<br>
  166. <small>Check this to allow cross-origin websocket requests</small></label>
  167. </div><br>
  168. <div class="upstreamActions">
  169. <!-- Change Priority -->
  170. <button class="ui basic circular icon button ${upArrowClass} highPriorityButton" title="Higher Priority"><i class="ui arrow up icon"></i></button>
  171. <button class="ui basic circular icon button lowPriorityButton" title="Lower Priority"><i class="ui arrow down icon"></i></button>
  172. <button class="ui basic circular icon button" title="Edit Upstream Destination"><i class="ui grey edit icon"></i></button>
  173. <button class="ui basic circular icon button" title="Remove Upstream"><i class="ui red trash icon"></i></button>
  174. </div>
  175. </div>`);
  176. });
  177. if (data.Origins.length == 1){
  178. $(".lowPriorityButton").addClass('disabled');
  179. }
  180. $(".ui.checkbox").checkbox();
  181. }else{
  182. //Assume no origins
  183. $("#inlineEditTable").html(`<tr>
  184. <td colspan="2"><i class="ui red circle times icon"></i> Invalid Upstream Settings</td>
  185. </tr>`);
  186. }
  187. }
  188. }
  189. })
  190. }
  191. /* New Upstream Origin Functions */
  192. //Clearn the proxy target value, make sure user do not enter http:// or https://
  193. //and auto select TLS checkbox if https:// exists
  194. function cleanProxyTargetValue(input){
  195. let targetDomain = $(input).val().trim();
  196. if (targetDomain.startsWith("http://")){
  197. targetDomain = targetDomain.substr(7);
  198. $(input).val(targetDomain);
  199. $("#requireTLS").parent().checkbox("set unchecked");
  200. }else if (targetDomain.startsWith("https://")){
  201. targetDomain = targetDomain.substr(8);
  202. $(input).val(targetDomain);
  203. $("#requireTLS").parent().checkbox("set checked");
  204. }else{
  205. //URL does not contains https or http protocol tag
  206. //sniff header
  207. $.ajax({
  208. url: "/api/proxy/tlscheck",
  209. data: {url: targetDomain},
  210. success: function(data){
  211. if (data.error != undefined){
  212. }else if (data == "https"){
  213. $("#requireTLS").parent().checkbox("set checked");
  214. }else if (data == "http"){
  215. $("#requireTLS").parent().checkbox("set unchecked");
  216. }
  217. }
  218. })
  219. }
  220. }
  221. //Add a new upstream to this http proxy rule
  222. function addNewUpstream(){
  223. let origin = $("#originURL").val().trim();
  224. let requireTLS = $("#requireTLS")[0].checked;
  225. let skipVerification = $("#skipTlsVerification")[0].checked;
  226. let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[0].checked;
  227. if (origin == ""){
  228. parent.msgbox("Upstream origin cannot be empty", false);
  229. return;
  230. }
  231. $.ajax({
  232. url: "/api/proxy/upstream/add",
  233. method: "POST",
  234. data:{
  235. "ep": editingEndpoint.ep,
  236. "origin": origin,
  237. "tls": requireTLS,
  238. "tlsval": skipVerification,
  239. "bpwsorg":skipWebSocketOriginCheck
  240. },
  241. success: function(data){
  242. if (data.error != undefined){
  243. parent.msgbox(data.error, false);
  244. }else{
  245. parent.msgbox("New upstream origin added");
  246. initOriginList();
  247. }
  248. }
  249. })
  250. }
  251. if (window.location.hash.length > 1){
  252. let payloadHash = window.location.hash.substr(1);
  253. try{
  254. payloadHash = JSON.parse(decodeURIComponent(payloadHash));
  255. $(".epname").text(payloadHash.ep);
  256. editingEndpoint = payloadHash;
  257. initOriginList();
  258. }catch(ex){
  259. console.log("Unable to load endpoint data from hash")
  260. }
  261. }
  262. function closeThisWrapper(){
  263. parent.hideSideWrapper(true);
  264. }
  265. </script>
  266. </body>
  267. </html>