Kaynağa Gözat

auto update script executed

Toby Chui 1 yıl önce
ebeveyn
işleme
8fbad247b5

+ 1 - 0
api.go

@@ -44,6 +44,7 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/proxy/status", ReverseProxyStatus)
 	authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
 	authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
+	authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
 	authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
 	authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
 	authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)

+ 36 - 0
helpers.go

@@ -2,7 +2,9 @@ package main
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
+	"time"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy"
 	"imuslab.com/zoraxy/mod/mdns"
@@ -10,9 +12,43 @@ import (
 	"imuslab.com/zoraxy/mod/utils"
 )
 
+/*
+	Proxy Utils
+*/
+//Check if site support TLS
+func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
+	targetURL, err := utils.PostPara(r, "url")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid url given")
+		return
+	}
+
+	httpsUrl := fmt.Sprintf("https://%s", targetURL)
+	httpUrl := fmt.Sprintf("http://%s", targetURL)
+
+	client := http.Client{Timeout: 5 * time.Second}
+
+	resp, err := client.Head(httpsUrl)
+	if err == nil && resp.StatusCode == http.StatusOK {
+		js, _ := json.Marshal("https")
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+
+	resp, err = client.Head(httpUrl)
+	if err == nil && resp.StatusCode == http.StatusOK {
+		js, _ := json.Marshal("http")
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+
+	utils.SendErrorResponse(w, "invalid url given")
+}
+
 /*
 	Statistic Summary
 */
+
 //Handle conversion of statistic daily summary to country summary
 func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
 	requestClientCountry := map[string]int{}

+ 6 - 5
web/components/blacklist.html

@@ -1,6 +1,9 @@
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Blacklist</h2>
+        <p>Setup blacklist based on estimated IP geographic location or IP address</p>
+    </div>
 
-<h3><i class="ui ban icon"></i> Blacklist</h3>
-<p>Setup blacklist based on estimated IP geographic location or IP address</p>
 <div class="ui divider"></div>
 <div class="ui toggle checkbox">
     <input type="checkbox" id="enableBlacklist">
@@ -12,7 +15,6 @@
 <div class="ui message">
     <i class="info circle icon"></i> Blacklist function require complex checking logic to validate each incoming request. Not recommend enabling this feature on servers with low end hardware.
 </div>
-<div class="ui divider"></div>
 <h4>Country Blacklist</h4>
     <p><i class="yellow exclamation triangle icon"></i>
     This will block all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
@@ -283,7 +285,6 @@
 
     </tbody>
 </table>
-<div class="ui divider"></div>
 
 <h4>IP Blacklist</h4>
 <p>Black a certain IP or IP range</p>
@@ -332,7 +333,7 @@
     </tbody>
   </table>
   <div class="pagination"></div>
-
+</div>
 <script>
     $(".dropdown").dropdown();
 

+ 80 - 75
web/components/cert.html

@@ -1,69 +1,73 @@
-
-<h3><i class="ui lock icon"></i> TLS / SSL Certificates</h3>
-<p>Setup TLS cert for different domains of your reverse proxy server names</p>
-<div class="ui divider"></div>
-<h4>Default Certificates</h4>
-    <small>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</small></p>
-    <table class="ui very basic celled table">
-        <thead>
-            <tr><th>Key Type</th>
-            <th>Exists</th>
-        </tr></thead>
-        <tbody>
-            <tr>
-                <td><i class="globe icon"></i> Default Public Key</td>
-                <td id="pubkeyExists"></td>
-            </tr>
-            <tr>
-                <td><i class="lock icon"></i> Default Private Key</td>
-                <td id="prikeyExists"></td>
-            </tr>
-        </tbody>
-    </table>
-<button class="ui button" onclick="uploadPublicKey();"><i class="globe icon"></i> Upload Public Key</button>
-<button class="ui black button" onclick="uploadPrivateKey();"><i class="lock icon"></i> Upload Private Key</button>
-<div class="ui divider"></div>
-<h4>Sub-domain Certificates</h4>
-<p>Provide certificates for multiple domains reverse proxy</p>
-<div class="ui fluid form">
-    <div class="three fields">
-        <div class="field">
-        <label>Server Name (Domain)</label>
-        <input type="text" id="certdomain" placeholder="example.com / blog.example.com">
-        </div>
-        <div class="field">
-            <label>Public Key</label>
-            <input type="file" id="pubkeySelector" onchange="handleFileSelect(event, 'pub')">
-        </div>
-        <div class="field">
-            <label>Private Key</label>
-            <input type="file" id="prikeySelector" onchange="handleFileSelect(event, 'pri')">
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>TLS / SSL Certificates</h2>
+        <p>Setup TLS cert for different domains of your reverse proxy server names</p>
+    </div>
+    
+    <div class="ui divider"></div>
+    <h4>Default Certificates</h4>
+        <small>When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one.<br>Note that you need both of them uploaded for it to fallback properly</small></p>
+        <table class="ui very basic unstackable celled table">
+            <thead>
+                <tr><th class="no-sort">Key Type</th>
+                <th  class="no-sort">Exists</th>
+            </tr></thead>
+            <tbody>
+                <tr>
+                    <td><i class="globe icon"></i> Default Public Key</td>
+                    <td id="pubkeyExists"></td>
+                </tr>
+                <tr>
+                    <td><i class="lock icon"></i> Default Private Key</td>
+                    <td id="prikeyExists"></td>
+                </tr>
+            </tbody>
+        </table>
+    <button class="ui basic button" onclick="uploadPublicKey();"><i class="globe icon"></i> Upload Public Key</button>
+    <button class="ui basic black button" onclick="uploadPrivateKey();"><i class="black lock icon"></i> Upload Private Key</button>
+    <div class="ui divider"></div>
+    <h4>Sub-domain Certificates</h4>
+    <p>Provide certificates for multiple domains reverse proxy</p>
+    <div class="ui fluid form">
+        <div class="three fields">
+            <div class="field">
+            <label>Server Name (Domain)</label>
+            <input type="text" id="certdomain" placeholder="example.com / blog.example.com">
+            </div>
+            <div class="field">
+                <label>Public Key</label>
+                <input type="file" id="pubkeySelector" onchange="handleFileSelect(event, 'pub')">
+            </div>
+            <div class="field">
+                <label>Private Key</label>
+                <input type="file" id="prikeySelector" onchange="handleFileSelect(event, 'pri')">
+            </div>
         </div>
+        <button class="ui basic button" onclick="handleDomainUploadByKeypress();"><i class="ui teal upload icon"></i> Upload</button>
     </div>
-    <button class="ui teal button" onclick="handleDomainUploadByKeypress();"><i class="ui upload icon"></i> Upload</button>
-</div>
-<div id="certUploadSuccMsg" class="ui green message" style="display:none;">
-    <i class="ui checkmark icon"></i> Certificate for domain <span id="certUploadingDomain"></span> uploaded.
-</div>
-<br>
-<div>
-    <table class="ui sortable unstackable celled table">
-        <thead>
-          <tr><th>Domain</th>
-          <th>Last Update</th>
-          <th class="no-sort">Remove</th>
-        </tr></thead>
-    <tbody id="certifiedDomainList">
+    <div id="certUploadSuccMsg" class="ui green message" style="display:none;">
+        <i class="ui checkmark icon"></i> Certificate for domain <span id="certUploadingDomain"></span> uploaded.
+    </div>
+    <br>
+    <div>
+        <table class="ui sortable unstackable celled table">
+            <thead>
+            <tr><th>Domain</th>
+            <th>Last Update</th>
+            <th class="no-sort">Remove</th>
+            </tr></thead>
+        <tbody id="certifiedDomainList">
 
-    </tbody>
-    </table>
-    <button class="ui green basic button" onclick="initManagedDomainCertificateList();"><i class="refresh icon"></i> Refresh List</button>
-</div>
-<div class="ui message">
-    <h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
-    If you have 3rd or even 4th level subdomains like <code>blog.example.com</code> or <code>en.blog.example.com</code> ,
-    depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
-    If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
+        </tbody>
+        </table>
+        <button class="ui basic button" onclick="initManagedDomainCertificateList();"><i class="green refresh icon"></i> Refresh List</button>
+    </div>
+    <div class="ui message">
+        <h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
+        If you have 3rd or even 4th level subdomains like <code>blog.example.com</code> or <code>en.blog.example.com</code> ,
+        depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
+        If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
+    </div>
 </div>
 <script>
     var uploadPendingPublicKey = undefined;
@@ -78,9 +82,10 @@
                 data: {domain: domain},
                 success: function(data){
                     if (data.error != undefined){
-                        alert(data.error);
+                        msgbox(data.error, false, 5000);
                     }else{
                         initManagedDomainCertificateList();
+                        initDefaultKeypairCheck();
                     }
                 }
             });
@@ -93,7 +98,7 @@
         $("#certifiedDomainList").html("");
         $.get("/api/cert/list?date=true", function(data){
             if (data.error != undefined){
-                alert(data.error);
+                msgbox(data.error, false, 5000);
             }else{
                 data.forEach(entry => {
                     $("#certifiedDomainList").append(`<tr>
@@ -127,7 +132,7 @@
     function handleDomainKeysUpload(callback=undefined){
         let domain = $("#certdomain").val();
         if (domain.trim() == ""){
-            alert("Missing domain.");
+            msgbox("Missing domain", false, 5000);
             return;
         }
         if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
@@ -142,7 +147,7 @@
             publicKeyRequest.onreadystatechange = function() {
             if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
                 if (publicKeyRequest.status !== 200) {
-                    alert('Error uploading public key: ' + publicKeyRequest.statusText);
+                    msgbox('Error uploading public key: ' + publicKeyRequest.statusText, false, 5000);
                 }
 
                 if (callback != undefined){
@@ -158,7 +163,7 @@
             privateKeyRequest.onreadystatechange = function() {
             if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
                 if (privateKeyRequest.status !== 200) {
-                    alert('Error uploading private key: ' + privateKeyRequest.statusText);
+                    msgbox('Error uploading private key: ' + privateKeyRequest.statusText, false, 5000);
                 }
                 if (callback != undefined){
                     callback();
@@ -167,7 +172,7 @@
             };
             privateKeyRequest.send(privateKeyForm);
         } else {
-            alert('One or both of the files is missing or not a file object');
+            msgbox('One or both of the files is missing or not a file object');
         }
     }
 
@@ -218,17 +223,17 @@
             .then(response => {
                 initDefaultKeypairCheck();
                 if (response.ok) {
-                    alert('File upload successful!');
+                    msgbox('File upload successful!');
                 } else {
                     response.text().then(text => {
-                        alert(text);
+                        msgbox(text, false, 5000);
                     });
                     //console.log(response.text());
                     //alert('File upload failed!');
                 }
                 })
             .catch(error => {
-                alert('An error occurred while uploading the file.');
+                msgbox('An error occurred while uploading the file.', false, 5000);
                 console.error(error);
             });
         });
@@ -257,18 +262,18 @@
             })
             .then(response => {
                 if (response.ok) {
-                    alert('File upload successful!');
+                    msgbox('File upload successful!');
                     initDefaultKeypairCheck();
                 } else {
                     response.text().then(text => {
-                        alert(text);
+                        msgbox(text, false, 5000);
                     });
                     //console.log(response.text());
                     //alert('File upload failed!');
                 }
                 })
             .catch(error => {
-                alert('An error occurred while uploading the file.');
+                msgbox('An error occurred while uploading the file.', false, 5000);
                 console.error(error);
             });
         });

+ 27 - 22
web/components/networktools.html

@@ -1,25 +1,30 @@
-<h3><i class="terminal icon"></i> Network Tools</h3>
-<p>Network tools to help manage your cluster nodes</p>
-
-<div class="ui top attached tabular menu">
-    <a class="nettools item active bluefont" data-tab="tab1">Discovery</a>
-    <a class="nettools item bluefont" data-tab="tab2">Connections</a>
-    <a class="nettools item bluefont" data-tab="tab3">Debugging</a>
-</div>
-
-<div class="ui bottom attached tab segment active" data-tab="tab1">
-    <h2>Multicast DNS (mDNS) Scanner</h2>
-    <p>Discover mDNS enabled service in this gateway forwarded network</p>
-    <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">View Discovery</button>
-    <div class="ui divider"></div>
-</div>
-
-<div class="ui bottom attached tab segment" data-tab="tab2">
-<p>Content of tab 2</p>
-</div>
-
-<div class="ui bottom attached tab segment" data-tab="tab3">
-<p>Content of tab 3</p>
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Network Tools</h2>
+        <p>Network tools to help manage your cluster nodes</p>
+    </div>
+
+
+    <div class="ui top attached tabular menu">
+        <a class="nettools item active bluefont" data-tab="tab1">Discovery</a>
+        <a class="nettools item bluefont" data-tab="tab2">Connections</a>
+        <a class="nettools item bluefont" data-tab="tab3">Debugging</a>
+    </div>
+
+    <div class="ui bottom attached tab segment active" data-tab="tab1">
+        <h2>Multicast DNS (mDNS) Scanner</h2>
+        <p>Discover mDNS enabled service in this gateway forwarded network</p>
+        <button class="ui basic larger circular button" onclick="launchToolWithSize('./tools/mdns.html',1000, 640);">View Discovery</button>
+        <div class="ui divider"></div>
+    </div>
+
+    <div class="ui bottom attached tab segment" data-tab="tab2">
+    <p>Content of tab 2</p>
+    </div>
+
+    <div class="ui bottom attached tab segment" data-tab="tab3">
+    <p>Content of tab 3</p>
+    </div>
 </div>
 
 <script>

+ 67 - 69
web/components/redirection.html

@@ -1,77 +1,76 @@
-<h3><i class="level up alternate icon"></i> Redirection Rules</h3>
-<p>Add exception case for redirecting any matching URLs</p>
-<div class="ui basic segment">
+<div class="standardContainer">
+  <div class="ui basic segment">
+    <h2>Redirection Rules</h2>
+    <p>Add exception case for redirecting any matching URLs</p>
+  </div>
   <div style="width: 100%; overflow-x: auto;">
-    <table class="ui sortable unstackable celled table" >
-      <thead>
-          <tr>
-              <th>Redirection URL</th>
-              <th class="no-sort"></th>
-              <th>Destination URL</th>
-              <th class="no-sort">Copy Pathname</th>
-              <th class="no-sort">Status Code</th>
-              <th class="no-sort">Remove</th>
-          </tr>
-      </thead>
-      <tbody id="redirectionRuleList">
-          <tr>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-          </tr>
-      </tbody>
-  </table>
+      <table class="ui sortable unstackable celled table" >
+        <thead>
+            <tr>
+                <th>Redirection URL</th>
+                <th>Destination URL</th>
+                <th class="no-sort">Copy Pathname</th>
+                <th class="no-sort">Status Code</th>
+                <th class="no-sort">Remove</th>
+            </tr>
+        </thead>
+        <tbody id="redirectionRuleList">
+            <tr>
+                <td></td>
+                <td></td>
+                <td></td>
+                <td></td>
+            </tr>
+        </tbody>
+    </table>
   </div>
-    
-    <div class="ui green message" id="delRuleSucc" style="display:none;">
-      <i class="ui green checkmark icon"></i> Redirection Rule Deleted
-    </div>
-</div>
-<div class="ui divider"></div>
-<p>Add path redirection to your domain</p>
-<div class="ui divider"></div>
-<div class="ui form">
-    <div class="field">
-      <label>Redirection URL</label>
-      <input type="text" id="rurl" name="redirection-url" placeholder="Redirection URL">
-      <small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be redirected to the destination URL, e.g. redirect.example.com</small>
-    </div>
-    <div class="field">
-      <label>Destination URL</label>
-      <input type="text" name="destination-url" placeholder="Destination URL">
-      <small><i class="ui circle info icon"></i> The target URL request being redirected to, e.g. dest.example.com/mysite</small>
-    </div>
-    <div class="field">
-      <div class="ui checkbox">
-        <input type="checkbox" name="forward-childpath" tabindex="0" class="hidden" checked>
-        <label>Forward Pathname</label>
+  <div class="ui green message" id="delRuleSucc" style="display:none;">
+    <i class="ui green checkmark icon"></i> Redirection Rule Deleted
+  </div>
+  <div class="ui divider"></div>
+  <h4>Add Redirection Rule</h4>
+  <div class="ui form">
+      <div class="field">
+        <label>Redirection URL (From)</label>
+        <input type="text" id="rurl" name="redirection-url" placeholder="Redirection URL">
+        <small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be redirected to the destination URL, e.g. redirect.example.com</small>
       </div>
-      <div class="ui message">
-        <p>Append the current pathname after the redirect destination</p>
-        <i class="check square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com<b>/blog?post=13</b> <br>
-        <i class="square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com
+      <div class="field">
+        <label>Destination URL (To)</label>
+        <input type="text" name="destination-url" placeholder="Destination URL">
+        <small><i class="ui circle info icon"></i> The target URL request being redirected to, e.g. dest.example.com/mysite</small>
       </div>
-    </div>
-    <div class="grouped fields">
-        <label>Redirection Status Code</label>
-        <div class="field">
-          <div class="ui radio checkbox">
-            <input type="radio" name="redirect-type" value="307" checked>
-            <label>Temporary Redirect <br><small>Status Code: 307</small></label>
-          </div>
+      <div class="field">
+        <div class="ui checkbox">
+          <input type="checkbox" name="forward-childpath" tabindex="0" class="hidden" checked>
+          <label>Forward Pathname</label>
         </div>
-        <div class="field">
-          <div class="ui radio checkbox">
-            <input type="radio" name="redirect-type" value="301">
-            <label>Moved Permanently <br><small>Status Code: 301</small></label>
-          </div>
+        <div class="ui message">
+          <p>Append the current pathname after the redirect destination</p>
+          <i class="check square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com<b>/blog?post=13</b> <br>
+          <i class="square outline icon"></i> old.example.com<b>/blog?post=13</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> new.example.com
         </div>
-    </div>
-    <button class="ui teal button" onclick="addRules();"><i class="ui plus icon"></i> Add Redirection Rule</button>
-    <div class="ui green message" id="ruleAddSucc" style="display:none;">
-      <i class="ui green checkmark icon"></i> Redirection Rules Added
-    </div>
+      </div>
+      <div class="grouped fields">
+          <label>Redirection Status Code</label>
+          <div class="field">
+            <div class="ui radio checkbox">
+              <input type="radio" name="redirect-type" value="307" checked>
+              <label>Temporary Redirect <br><small>Status Code: 307</small></label>
+            </div>
+          </div>
+          <div class="field">
+            <div class="ui radio checkbox">
+              <input type="radio" name="redirect-type" value="301">
+              <label>Moved Permanently <br><small>Status Code: 301</small></label>
+            </div>
+          </div>
+      </div>
+      <button class="ui basic button" onclick="addRules();"><i class="ui teal plus icon"></i> Add Redirection Rule</button>
+      <div class="ui green message" id="ruleAddSucc" style="display:none;">
+        <i class="ui green checkmark icon"></i> Redirection Rules Added
+      </div>
+  </div>
 </div>
 <script>
     $(".checkbox").checkbox();
@@ -136,7 +135,6 @@
             data.forEach(function(entry){
                 $("#redirectionRuleList").append(`<tr>
                     <td>${entry.RedirectURL} </td>
-                    <td style="text-align: center;"><i class="blue arrow right icon"></i></td>
                     <td>${entry.TargetURL}</td>
                     <td>${entry.ForwardChildpath?"<i class='ui green checkmark icon'></i>":"<i class='ui red remove icon'></i>"}</td>
                     <td>${entry.StatusCode==307?"Temporary Redirect (307)":"Moved Permanently (301)"}</td>

+ 37 - 19
web/components/rproot.html

@@ -1,24 +1,24 @@
-<h3><i class="ui home icon"></i> Set Proxy Root</h3>
-<p>For all routing not found in the proxy rule, will be redirected to the proxy root server.</p>
-<div class="ui form">
-    <div class="field">
-        <label>Proxy Root</label>
-        <input type="text" id="proxyRoot">
-        <small>E.g. localhost:8080</small>
-    </div>
-    <div class="field">
-        <div class="ui checkbox">
-            <input type="checkbox" id="rootReqTLS">
-            <label>Root require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Set Proxy Root</h2>
+        <p>For all routing not found in the proxy rules, request will be redirected to the proxy root server.</p>
+        <div class="ui form">
+            <div class="field">
+                <label>Proxy Root</label>
+                <input type="text" id="proxyRoot" onchange="checkRootRequireTLS(this.value);">
+                <small>E.g. localhost:8080</small>
+            </div>
+            <div class="field">
+                <div class="ui checkbox">
+                    <input type="checkbox" id="rootReqTLS" >
+                    <label>Root require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+                </div>
+            </div>
         </div>
+        <br>
+        <button class="ui basic button" onclick="setProxyRoot()"><i class="teal home icon" ></i> Update Proxy Root</button>
     </div>
 </div>
-<br>
-<button class="ui teal button" onclick="setProxyRoot()"><i class="home icon" ></i> Set Proxy Root</button>
-<div class="ui green message" id="ProxyRootUpdate" style="display:none">
-    <i class="ui checkmark icon"></i> Proxy Root Updated
-</div>
-
 <script>
     function initRootInfo(){
         $.get("/api/proxy/list?type=root", function(data){
@@ -26,11 +26,29 @@
 
             }else{
                 $("#proxyRoot").val(data.Domain);
+                checkRootRequireTLS(data.Domain);
             }
         });
     }
     initRootInfo();
 
+    function checkRootRequireTLS(targetDomain){
+       $.ajax({
+            url: "/api/proxy/tlscheck",
+            data: {url: targetDomain},
+            success: function(data){
+                if (data.error != undefined){
+
+                }else if (data == "https"){
+                    $("#rootReqTLS").parent().checkbox("set checked");
+                }else if (data == "http"){
+                    $("#rootReqTLS").parent().checkbox("set unchecked");
+                }
+            }
+       })
+    }
+
+
     function setProxyRoot(){
         var newpr = $("#proxyRoot").val();
         if (newpr.trim() == ""){
@@ -52,7 +70,7 @@
                 }else{
                     //OK
                     initRootInfo();
-                    $("#ProxyRootUpdate").stop().slideDown('fast').delay(3000).slideUp('fast');
+                    msgbox("Proxy Root Updated")
                 }
             }
         });

+ 100 - 48
web/components/rules.html

@@ -1,45 +1,59 @@
-<h3><i class="ui exchange icon"></i> New Proxy Endpoint</h3>
-<p>You can create a proxy endpoing by subdomain or virtual directories</p>
-<div class="ui form">
-    <div class="field">
-        <label>Proxy Type</label>
-        <div class="ui selection dropdown">
-            <input type="hidden" id="ptype" value="subd">
-            <i class="dropdown icon"></i>
-            <div class="default text">Proxy Type</div>
-            <div class="menu">
-                <div class="item" data-value="subd">Sub-domain</div>
-                <div class="item" data-value="vdir">Virtual Directory</div>
+<div class="ui stackable grid">
+        <div class="ten wide column">
+            <div class="standardContainer">
+                <div class="ui basic segment" style="margin-top: 1em;">
+                    <h2>New Proxy Rule</h2>
+                    <p>You can create a proxy endpoing by subdomain or virtual directories</p>
+                    <div class="ui form">
+                        <div class="field">
+                            <label>Proxy Type</label>
+                            <div class="ui selection dropdown">
+                                <input type="hidden" id="ptype" value="subd">
+                                <i class="dropdown icon"></i>
+                                <div class="default text">Proxy Type</div>
+                                <div class="menu">
+                                    <div class="item" data-value="subd">Sub-domain</div>
+                                    <div class="item" data-value="vdir">Virtual Directory</div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="field">
+                            <label>Subdomain Matching Keyword / Virtual Directory Name</label>
+                            <input type="text" id="rootname" placeholder="s1.mydomain.com">
+                        
+                        </div>
+                        <div class="field">
+                            <label>IP Address or Domain Name with port</label>
+                            <input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
+                            <small>E.g. 192.168.0.101:8000 or example.com</small>
+                        </div>
+                        <div class="field">
+                            <div class="ui checkbox">
+                                <input type="checkbox" id="reqTls">
+                                <label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+                            </div>
+                        </div>
+                        <button class="ui basic button" onclick="newProxyEndpoint();"><i class="blue add icon"></i> Create Endpoint</button>
+                        <br><br>
+                    </div>
+                </div>
             </div>
         </div>
-    </div>
-    <div class="field">
-        <label>Subdomain Matching Keyword / Virtual Directory Name</label>
-        <input type="text" id="rootname" placeholder="s1.mydomain.com">
-        <div class="ui message">
-            Example of subdomain matching keyword:<br>
-            <code>s1.arozos.com</code> <br>(Any access starting with s1.arozos.com will be proxy to the IP address below)<br>
-            Example of virtual directory name: <br>
-            <code>/s1/home</code> <br>(Any access to {this_server}/s1/ will be proxy to the IP address below)
-        </div>
-    </div>
-    <div class="field">
-        <label>IP Address or Domain Name with port</label>
-        <input type="text" id="proxyDomain">
-        <small>E.g. 192.168.0.101:8000 or example.com</small>
-    </div>
-    <div class="field">
-        <div class="ui checkbox">
-            <input type="checkbox" id="reqTls">
-            <label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+        <div class="six wide column">
+            <div class="ui basic segment" style="height: 100%; background-color: var(--theme_grey); color: var(--theme_lgrey);">
+                <br>
+                <span style="font-size: 1.2em; font-weight: 300;">Subdomain</span><br>
+                Example of subdomain matching keyword:<br>
+                <code>s1.arozos.com</code> <br>(Any access starting with s1.arozos.com will be proxy to the IP address below)<br>
+                <div class="ui divider"></div>
+                <span style="font-size: 1.2em; font-weight: 300;">Virtual Directory</span><br>
+                Example of virtual directory name: <br>
+                <code>/s1/home</code> <br>(Any access to {this_server}/s1/ will be proxy to the IP address below)<br>
+                <br>
+            </div>
         </div>
     </div>
-    <button class="ui teal button" onclick="newProxyEndpoint();">Create Proxy Endpoint</button>
-</div>
-<div class="ui green message" id="proxyaddSucc" style="display:none">
-    <i class="ui checkmark icon"></i> Proxy Endpoint Added
 </div>
-
 <script>
     //New Proxy Endpoint
     function newProxyEndpoint(){
@@ -48,6 +62,20 @@
         var proxyDomain = $("#proxyDomain").val();
         var useTLS = $("#reqTls")[0].checked;
 
+        if (type === "vdir") {
+            if (!rootname.startsWith("/")) {
+                rootname = "/" + rootname
+                $("#rootname").val(rootname);
+            }
+        }else{
+            if (!isSubdomainDomain(rootname)){
+                //This doesn't seems like a subdomain
+                if (!confirm(rootname + " does not looks like a subdomain. Continue anyway?")){
+                    return;
+                }   
+            }
+        }
+
         if (rootname.trim() == ""){
             $("#rootname").parent().addClass("error");
             return
@@ -68,12 +96,12 @@
             data: {type: type, rootname: rootname, tls: useTLS, ep: proxyDomain},
             success: function(data){
                 if (data.error != undefined){
-                    alert(data.error);
+                    msgbox(data.error, false, 5000);
                 }else{
                     //OK
                     listVdirs();
                     listSubd();
-                    $("#proxyaddSucc").stop().slideDown('fast').delay(3000).slideUp('fast');
+                    msgbox("Proxy Endpoint Added");
 
                     //Clear old data
                     $("#rootname").val("");
@@ -86,15 +114,39 @@
 
     //Generic functions for delete rp endpoints 
     function deleteEndpoint(ptype, epoint){
-                if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
-                    $.ajax({
-                        url: "/api/proxy/del",
-                        data: {ep: epoint, ptype: ptype},
-                        success: function(){
-                            listVdirs();
-                            listSubd();
-                        }
-                    })
+        if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
+            $.ajax({
+                url: "/api/proxy/del",
+                data: {ep: epoint, ptype: ptype},
+                success: function(){
+                    listVdirs();
+                    listSubd();
+                }
+            })
+        }
+    }
+
+
+    function autoCheckTls(targetDomain){
+       $.ajax({
+            url: "/api/proxy/tlscheck",
+            data: {url: targetDomain},
+            success: function(data){
+                if (data.error != undefined){
+
+                }else if (data == "https"){
+                    $("#reqTls").parent().checkbox("set checked");
+                }else if (data == "http"){
+                    $("#reqTls").parent().checkbox("set unchecked");
                 }
             }
+       })
+    }
+
+
+    //Check if a string is a valid subdomain 
+    function isSubdomainDomain(str) {
+        const regex = /^(localhost|[a-z0-9]+([\-.]{1}[a-z0-9]+)*\.[a-z]{2,}|[a-z0-9]+([\-.]{1}[a-z0-9]+)*\.[a-z]{2,}\.)$/i;
+        return regex.test(str);
+    } 
 </script>

+ 83 - 89
web/components/status.html

@@ -1,11 +1,11 @@
 <div class="ui stackable grid">
-    <div class="ten wide column">
-        <div id="serverstatus" class="ui green statustab inverted segment">
+    <div class="ten wide column serverstatusWrapper">
+        <div id="serverstatus" class="ui statustab inverted segment">
             <h1 class="ui header" style="margin-top: 1em; margin-left: 0.4em; padding-bottom: 1em;">
-                <i class="power off icon"></i>
+                <i id="rpStatusIcon" class="loading spinner icon"></i>
                 <div class="content">
-                    <span id="statusTitle">Offline</span>
-                    <div class="sub header" id="statusText">Reverse proxy server is offline</div>
+                    <span id="statusTitle">Loading</span>
+                    <div class="sub header" id="statusText">Checking server status</div>
                 </div>
             </h1>
             <div class="dot-container">
@@ -16,7 +16,7 @@
             </div>
         </div>
     </div>
-    <div class="six wide column">
+    <div class="six wide column statisticWrapper">
         <div class="ui greybackground statustab segment">
             <h5 class="ui header">
                 <i class="exchange icon"></i>
@@ -51,77 +51,74 @@
         </div>
     </div>
 </div>
-<div class="ui divider"></div>
-<h4>Basic Settings</h4>
-<p>Inbound Port (Port to be proxied)</p>
-<div class="ui action fluid notloopbackOnly input">
-    <input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
-    <button class="ui basic green notloopbackOnly button" onclick="handlePortChange();">Apply</button>
-</div>
-<br>
-<div id="tls" class="ui toggle notloopbackOnly checkbox">
-    <input type="checkbox">
-    <label>Use TLS to serve proxy request</label>
-</div>
 <br>
-<div id="redirect" class="ui toggle notloopbackOnly checkbox" style="margin-top: 0.6em;">
-    <input type="checkbox">
-    <label>Force redirect HTTP request to HTTPS<br>
-        <small>(Only apply when listening port is not 80)</small></label>
-</div>
-<br>
-<div id="portUpdateSucc" class="ui green message" style="display:none;">
-    <i class="ui green checkmark icon"></i> Setting Updated
-</div>
-<Br>
-<button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
-<button id="stopbtn" class="ui red notloopbackOnly disabled button" onclick="stopService();">Stop Service</button>
-<div id="rploopbackWarning" class="ui segment" style="display:none;">
-    <b><i class="yellow warning icon"></i> Loopback Routing Warning</b><br>
-    <small>This management interface is a loopback proxied service. <br>If you want to shutdown the reverse proxy server, please remove the proxy rule for the management interface and refresh.</small>
-</div>
-<div id="statusErrmsg" class="ui red message" style="display: none;"></div>
-<div class="ui divider"></div>
-<div class="">
-<h4>Statistic Overview</h4>
-<div class="ui two column stackable grid">
-    <div class="column">
-      <p>Visitor Counts</p>
-      <table class="ui unstackable inverted celled table">
-        <thead>
-          <tr>
-            <th>Country ISO Code</th>
-            <th>Unique Visitors</th>
-          </tr>
-        </thead>
-        <tbody id="countryCodetable">
+<div class="standardContainer">
+    <h4>Basic Settings</h4>
+    <p>Inbound Port (Port to be proxied)</p>
+    <div class="ui action fluid notloopbackOnly input">
+        <input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
+        <button class="ui basic green notloopbackOnly button" onclick="handlePortChange();">Apply</button>
+    </div>
+    <br>
+    <div id="tls" class="ui toggle notloopbackOnly checkbox">
+        <input type="checkbox">
+        <label>Use TLS to serve proxy request</label>
+    </div>
+    <br>
+    <div id="redirect" class="ui toggle notloopbackOnly checkbox" style="margin-top: 0.6em;">
+        <input type="checkbox">
+        <label>Force redirect HTTP request to HTTPS<br>
+            <small>(Only apply when listening port is not 80)</small></label>
+    </div>
+    <br><br>
+    <button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
+    <button id="stopbtn" class="ui red notloopbackOnly disabled button" onclick="stopService();">Stop Service</button>
+    <div id="rploopbackWarning" class="ui segment" style="display:none;">
+        <b><i class="yellow warning icon"></i> Loopback Routing Warning</b><br>
+        <small>This management interface is a loopback proxied service. <br>If you want to shutdown the reverse proxy server, please remove the proxy rule for the management interface and refresh.</small>
+    </div>
+    <div class="ui divider"></div>
+    <div class="">
+    <h4>Statistic Overview</h4>
+    <div class="ui two column stackable grid">
+        <div class="column">
+        <p>Visitor Counts</p>
+        <table class="ui unstackable inverted celled table">
+            <thead>
             <tr>
-                <td colspan="2">No Data</td>
+                <th>Country ISO Code</th>
+                <th>Unique Visitors</th>
             </tr>
-        </tbody>
-      </table>
-    </div>
-    <div class="column">
-      <p>Proxy Request Types</p>
-      <table class="ui unstackable inverted celled table">
-        <thead>
-          <tr>
-            <th>Proxy Type</th>
-            <th>Count</th>
-          </tr>
-        </thead>
-        <tbody id="forwardTypeTable">
+            </thead>
+            <tbody id="countryCodetable">
+                <tr>
+                    <td colspan="2">No Data</td>
+                </tr>
+            </tbody>
+        </table>
+        </div>
+        <div class="column">
+        <p>Proxy Request Types</p>
+        <table class="ui unstackable inverted celled table">
+            <thead>
             <tr>
-                <td colspan="2">No Data</td>
+                <th>Proxy Type</th>
+                <th>Count</th>
             </tr>
-        </tbody>
-      </table>
+            </thead>
+            <tbody id="forwardTypeTable">
+                <tr>
+                    <td colspan="2">No Data</td>
+                </tr>
+            </tbody>
+        </table>
+        </div>
     </div>
-  </div>
+    </div>
+    <br>
+    <button class="ui right floated basic button" onclick="getDailySummaryDetails();"><i class="green refresh icon"></i> Refresh</button>
+    <br><br>
 </div>
-<br>
-<button class="ui right floated basic button" onclick="getDailySummaryDetails();"><i class="green refresh icon"></i> Refresh</button>
-<br><br>
 <script>
     let loopbackProxiedInterface = false;
     //Initial the start stop button if this is reverse proxied
@@ -145,11 +142,13 @@
                 }
                 $("#serverstatus").addClass("green");
                 $("#statusTitle").text("Online");
+                $("#rpStatusIcon").attr("class", "green circle check icon");
                 $("#statusText").text("Serving request on port: " + data.Option.Port);
             }else{
                 $("#startbtn").removeClass("disabled");
                 $("#stopbtn").addClass("disabled");
                 $("#statusTitle").text("Offline");
+                $("#rpStatusIcon").attr("class", "black circle times icon")
                 $("#statusText").text("Reverse proxy server is offline");
                 $("#serverstatus").removeClass("green");
             }
@@ -197,8 +196,8 @@
             $("#country").html((Object.keys(data)[0])?Object.keys(data)[0]:"No Data");
             $("#countryList").html(`
             <div>
-                ${(Object.keys(data)[1])?Object.keys(data)[1]:"No Data"}<br>
-                ${(Object.keys(data)[2])?Object.keys(data)[2]:"No Data"}
+                ${(Object.keys(data)[1])?Object.keys(data)[1]:"-"}<br>
+                ${(Object.keys(data)[2])?Object.keys(data)[2]:"-"}
             </div>
             `);
 
@@ -213,7 +212,7 @@
 
             if (Object.keys(data).length == 0){
                 $("#countryCodetable").append(`<tr>
-                    <td colspan="2">No Data</td>
+                    <td colspan="2"><i class="ui green circle check icon"></i> No Data</td>
                 </tr>`);
             }
             
@@ -235,8 +234,8 @@
             $("#forwardtype").html((Object.keys(data)[0])?fft(Object.keys(data)[0]) + ": " + abbreviateNumber(data[Object.keys(data)[0]]):"No Data");
             $("#forwardtypeList").html(`
             <div>
-                ${(Object.keys(data)[1])?fft(Object.keys(data)[1]) + ": " + abbreviateNumber(data[Object.keys(data)[1]]):"No Data"}<br>
-                ${(Object.keys(data)[2])?fft(Object.keys(data)[2]) + ": " + abbreviateNumber(data[Object.keys(data)[2]]):"No Data"}
+                ${(Object.keys(data)[1])?fft(Object.keys(data)[1]) + ": " + abbreviateNumber(data[Object.keys(data)[1]]):"-"}<br>
+                ${(Object.keys(data)[2])?fft(Object.keys(data)[2]) + ": " + abbreviateNumber(data[Object.keys(data)[2]]):"-"}
             </div>
             `);
 
@@ -250,7 +249,7 @@
 
             if (Object.keys(data).length == 0){
                 $("#forwardTypeTable").append(`<tr>
-                    <td colspan="2">No Data</td>
+                    <td colspan="2"><i class="ui green circle check icon"></i> No Data</td>
                 </tr>`);
             }
         });
@@ -276,7 +275,7 @@
     function startService(){
         $.post("/api/proxy/enable", {enable: true}, function(data){
             if (data.error != undefined){
-                statusErrmsg(data.error);
+                msgbox(data.error, false, 5000);
             }
             initRPStaste();
         });
@@ -285,30 +284,25 @@
     function stopService(){
         $.post("/api/proxy/enable", {enable: false}, function(data){
             if (data.error != undefined){
-                statusErrmsg(data.error);
+                msgbox(data.error, false, 5000);
             }
             initRPStaste();
         });
     }
 
-    //Show error message
-    function statusErrmsg(message){
-        $("#statusErrmsg").html(`<i class="red remove icon"></i> ${message}`);
-        $("#statusErrmsg").slideDown('fast').delay(5000).slideUp('fast');
-    }
 
     function handlePortChange(){
         var newPortValue = $("#incomingPort").val();
-        if (isNaN(newPortValue - 1)){
-            alert("Invalid incoming port value");
+        if (isNaN(newPortValue - 1) || newPortValue < 1 || newPortValue > 65535){
+            msgbox("Invalid incoming port value", false, 5000);
             return;
         }
 
         $.post("/api/proxy/setIncoming", {incoming: newPortValue}, function(data){
             if (data.error != undefined){
-                statusErrmsg(data.error);
+                msgbox(data.error, false, 5000);
             }
-            $("#portUpdateSucc").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
+            msgbox("Setting Updated");
             initRPStaste();
         });
     }
@@ -330,7 +324,7 @@
                                 alert(data.error);
                             }else{
                                 //Updated
-                                $("#portUpdateSucc").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
+                                msgbox("Setting Updated");
                                 initRPStaste();
                             }
                         }
@@ -366,7 +360,7 @@
                             alert(data.error);
                         }else{
                             //Updated
-                            $("#portUpdateSucc").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
+                            msgbox("Setting Updated");
                             initRPStaste();
                         }
                     }

+ 20 - 13
web/components/subd.html

@@ -1,16 +1,23 @@
-<table class="ui celled sortable unstackable compact table">
-    <thead>
-        <tr>
-            <th>Matching Domain</th>
-            <th>Proxy To</th>
-            <th class="no-sort">Remove</th>
-        </tr>
-    </thead>
-    <tbody id="subdList">
-       
-    </tbody>
-</table>
-<button class="ui icon green basic button" onclick="listSubd();"><i class="refresh icon"></i> Refresh</button>
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Subdomain</h2>
+        <p>Subdomains are a way to organize and identify different sections of a website or domain. They are essentially a prefix to the main domain name, separated by a dot. <br>For example, in the domain "blog.example.com," "blog" is the subdomain.</p>
+    </div>
+    <table class="ui celled sortable unstackable compact table">
+        <thead>
+            <tr>
+                <th>Matching Domain</th>
+                <th>Proxy To</th>
+                <th class="no-sort">Remove</th>
+            </tr>
+        </thead>
+        <tbody id="subdList">
+        
+        </tbody>
+    </table>
+    <button class="ui icon right floated basic button" onclick="listSubd();"><i class="green refresh icon"></i> Refresh</button>
+    <br><br>
+</div>
 <script>
     listSubd();
     function listSubd(){

+ 18 - 26
web/components/uptime.html

@@ -1,32 +1,24 @@
-<h3><i class="clock green icon"></i> Uptime Monitor</h3>
-<p>Check the online state of proxied targets</p>
-<!--
-<div class="ui toggle checkbox" id="utmEnable">
-    <input type="checkbox" name="utmEnable">
-    <label>Enable External Access</label>
-</div>
-<div class="ui message">
-    You can expose the uptime monitor interface to public by adding: <br>
-    <code>%uptime_monitor%</code> 
-    <br>as a subdomain or virtual directory target URL in the "Create Proxy Rules" tab.
-</div>
--->
-<div class="ui divider"></div>
-<div id="utmrender" class="ui basic segment">
+<div class="standardContainer">
     <div class="ui basic segment">
-        <h4 class="ui header">
-            <i class="red remove icon"></i>
-            <div class="content">
-                Uptime Monitoring service is currently unavailable
-                <div class="sub header">This might cause by an error in cluster communication within the host servers. Please wait for administrator to resolve the issue.</div>
-            </div>
-        </h4> 
+        <h2>Uptime Monitor</h2>
+        <p>Check the online state of proxied targets</p>
+    </div>
+    <div class="ui divider"></div>
+    <div id="utmrender" class="ui basic segment">
+        <div class="ui basic segment">
+            <h4 class="ui header">
+                <i class="red remove icon"></i>
+                <div class="content">
+                    Uptime Monitoring service is currently unavailable
+                    <div class="sub header">This might cause by an error in cluster communication within the host servers. Please wait for administrator to resolve the issue.</div>
+                </div>
+            </h4> 
+        </div>
+    </div>
+    <div align="center">
+        <button class="ui basic circular green icon button" onclick="reloadUptimeList();"><i class="refresh icon"></i></button>   
     </div>
 </div>
-<div align="center">
-    <button class="ui basic circular green icon button" onclick="reloadUptimeList();"><i class="refresh icon"></i></button>   
-</div>
-
 
 
 <script>

+ 52 - 49
web/components/utils.html

@@ -1,61 +1,64 @@
-<h3><i class="paperclip icon"></i> Utilities</h3>
-<p>You might find these tools helpful when setting up your gateway server</p>
-<div class="ui divider"></div>
-<div class="selfauthOnly">
-    <h3><i class="ui user icon"></i> Account Management</h3>
-    <p>Functions to help management the current account</p>
+<div class="standardContainer">
     <div class="ui basic segment">
-        <h5><i class="chevron down icon"></i> Change Password</h5>
-        <div class="ui form">
-            <div class="field">
-                <label>Current Password</label>
-                <input type="password" name="oldPassword" placeholder="Current Password">
-            </div>
-            <div class="field">
-                <label>New Password</label>
-                <input type="password" name="newPassword" placeholder="New Password">
-            </div>
-            <div class="field">
-                <label>Confirm New Password</label>
-                <input type="password" name="confirmNewPassword" placeholder="Confirm New Password">
+        <h2>Utilities</h2>
+        <p>You might find these tools helpful when setting up your gateway server</p>
+    </div>
+    <div class="ui divider"></div>
+    <div class="selfauthOnly">
+        <h3><i class="ui user icon"></i> Account Management</h3>
+        <p>Functions to help management the current account</p>
+        <div class="ui basic segment">
+            <h5><i class="chevron down icon"></i> Change Password</h5>
+            <div class="ui form">
+                <div class="field">
+                    <label>Current Password</label>
+                    <input type="password" name="oldPassword" placeholder="Current Password">
+                </div>
+                <div class="field">
+                    <label>New Password</label>
+                    <input type="password" name="newPassword" placeholder="New Password">
+                </div>
+                <div class="field">
+                    <label>Confirm New Password</label>
+                    <input type="password" name="confirmNewPassword" placeholder="Confirm New Password">
+                </div>
+                <button class="ui basic button" onclick="changePassword()"><i class="ui teal key icon"></i> Change Password</button>
             </div>
-            <button class="ui teal button" onclick="changePassword()"><i class="ui key icon"></i> Change Password</button>
-        </div>
 
-        <div id="passwordChangeSuccMsg" class="ui green message" style="display:none;">
-            <i class="ui circle checkmark green icon "></i> Password Updated
+            <div id="passwordChangeSuccMsg" class="ui green message" style="display:none;">
+                <i class="ui circle checkmark green icon "></i> Password Updated
+            </div>
         </div>
+        <div class="ui divider"></div>
     </div>
-    <div class="ui divider"></div>
-</div>
-  
-<h3><i class="ui code icon"></i> IP Address Converter</h3>
-<p>No experience with CIDR notations? Here are some tools you can use to make setting up easier.</p>
-<div class="ui basic segment">
-    <h5><i class="chevron down icon"></i> IP Range to CIDR Conversion</h5>
-    <div class="ui message">
-        <i class="info circle icon"></i> Note that the CIDR generated here covers additional IP address before or after the given range. If you need more details settings, please use CIDR with a smaller range and add additional IPs for detail range adjustment.
-    </div>
-    <div class="ui input">
-        <input type="text" placeholder="Start IP" id="startIpInput">
-    </div>
-    <div class="ui input">
-        <input type="text" placeholder="End IP" id="endIpInput">
+    
+    <h3><i class="ui code icon"></i> IP Address Converter</h3>
+    <p>No experience with CIDR notations? Here are some tools you can use to make setting up easier.</p>
+    <div class="ui basic segment">
+        <h5><i class="chevron down icon"></i> IP Range to CIDR Conversion</h5>
+        <div class="ui message">
+            <i class="info circle icon"></i> Note that the CIDR generated here covers additional IP address before or after the given range. If you need more details settings, please use CIDR with a smaller range and add additional IPs for detail range adjustment.
+        </div>
+        <div class="ui input">
+            <input type="text" placeholder="Start IP" id="startIpInput">
+        </div>
+        <div class="ui input">
+            <input type="text" placeholder="End IP" id="endIpInput">
+        </div>
+        <br>
+        <button style="margin-top: 0.6em;" class="ui basic button" onclick="convertToCIDR()">Convert</button>
+        <p>Results: <div id="cidrOutput">N/A</div></p>
     </div>
-    <br>
-    <button style="margin-top: 0.6em;" class="ui button" onclick="convertToCIDR()">Convert</button>
-    <p>Results: <div id="cidrOutput">N/A</div></p>
-</div>
 
-<div class="ui basic segment">
-    <h5><i class="chevron down icon"></i> CIDR to IP Range Conversion</h5>
-    <div class="ui action input">
-        <input type="text" placeholder="CIDR" id="cidrInput">
-        <button class="ui button" onclick="convertToIPRange()">Convert</button>
+    <div class="ui basic segment">
+        <h5><i class="chevron down icon"></i> CIDR to IP Range Conversion</h5>
+        <div class="ui action input">
+            <input type="text" placeholder="CIDR" id="cidrInput">
+            <button class="ui basic button" onclick="convertToIPRange()">Convert</button>
+        </div>
+        <p>Results: <div id="ipRangeOutput">N/A</div></p>
     </div>
-    <p>Results: <div id="ipRangeOutput">N/A</div></p>
 </div>
-
 <script>
 
     /*

+ 22 - 17
web/components/vdir.html

@@ -1,20 +1,25 @@
-<table class="ui celled sortable unstackable compact table">
-    <thead>
-        <tr>
-            <th>Virtual Directory</th>
-            <th>Proxy To</th>
-            <th class="no-sort">Remove</th>
-        </tr>
-    </thead>
-    <tbody id="vdirList">
-        <tr>
-            <td data-label="">test</td>
-            <td data-label="">test</td>
-            <td data-label=""><button class="ui circular mini red basic button"><i class="remove icon"></i> Remove Proxy</button></td>
-        </tr>
-    </tbody>
-</table>
-<button class="ui icon green basic button" onclick="listVdirs();"><i class="refresh icon"></i> Refresh</button>
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Virtual Directory</h2>
+        <p>A virtual directory is a consolidated view of multiple directories that provides a unified entry point for users to access disparate sources.</p>
+    </div>
+    <table class="ui celled sortable unstackable compact table">
+        <thead>
+            <tr>
+                <th>Virtual Directory</th>
+                <th>Proxy To</th>
+                <th class="no-sort">Remove</th>
+            </tr>
+        </thead>
+        <tbody id="vdirList">
+            <tr>
+                <td data-label=""><button class="ui circular mini red basic button"><i class="remove icon"></i> Remove Proxy</button></td>
+            </tr>
+        </tbody>
+    </table>
+    <button class="ui icon right floated basic button" onclick="listVdirs();"><i class="green refresh icon"></i> Refresh</button>
+    <br><br>
+</div>
 <script>
      //Virtual directories functions
     listVdirs();

+ 17 - 1
web/index.html

@@ -124,7 +124,11 @@
         <div class="ui container" style="color: grey; font-size: 90%">
             <p>CopyRight Zoraxy project and its author, 2022 - <span class="year"></span></p>
         </div>
-           
+        
+        <div id="messageBox" class="ui green floating big compact message">
+            <p><i class="green check circle icon"></i> There are no message</p>
+        </div>
+        
         <br><br>
         <script>
             $(".year").text(new Date().getFullYear());
@@ -235,6 +239,18 @@
                 }
             });
 
+            function msgbox(message, succ=true, delayDuration=3000){
+                let icon = `<i class="ui info circle icon"></i>`;
+                if (succ){
+                    $("#messageBox").attr("class", "ui green floating compact message")
+                    icon = `<i class="ui green circle check icon"></i>`;
+                }else{
+                    $("#messageBox").attr("class", "ui red floating compact message")
+                    icon = `<i class="ui red circle times icon"></i>`;
+                }
+                $("#messageBox").html(`${icon} ${message}`);
+                $("#messageBox").stop().finish().fadeIn("fast").delay(delayDuration).fadeOut("fast");
+            }
 
         </script>
     </body>

+ 109 - 11
web/main.css

@@ -1,13 +1,19 @@
 /*
     index.html style overwrite
 */
+:root{
+    --theme_grey: #414141;
+    --theme_lgrey: #f6f6f6;
+    --theme_green: #3c9c63;
+    --theme_fcolor: #979797;
+}
 body{
     background-color:#f6f6f6;
+    color: #414141;
 }
 
 .functiontab{
     display:none;
-    padding: 1em;
 }
 
 .menubar{
@@ -46,7 +52,7 @@ body{
 }
 
 .contentWindow{
-    padding: 1em;
+    /*padding: 1em;*/
     flex: 1;
     background-color: white;
     border-radius: 1em;
@@ -62,6 +68,74 @@ body{
     font-weight: 300;
 }
 
+.serverstatusWrapper{
+    padding-right: 0 !important;
+}
+
+.statisticWrapper{
+    padding-left: 0 !important;
+}
+
+/* Message Box */
+#messageBox{
+    position: fixed;
+    bottom: 1em;
+    right: 1em;
+    display:none;
+    max-width: 300px;
+}
+
+/* Standard containers */
+.standardContainer.noleftright{
+    padding-left: 0;
+    padding-right: 0;
+}
+
+.standardContainer.noleft{
+    padding-left: 0;
+}
+
+.standardContainer.noright{
+    padding-right: 0;
+}
+
+.standardContainer.notopdown{
+    padding-top: 0;
+    padding-bottom: 0;
+}
+
+.standardContainer.notop{
+    padding-top: 0;
+}
+
+.standardContainer.nobottom{
+    padding-bottom: 0;
+}
+
+
+/*
+    RWD Rules
+*/
+
+
+@media screen and (min-width: 750px) {
+    #serverstatus{
+        border-top-left-radius: 1em !important;
+    }
+
+    .greybackground.statustab{
+        border-top-right-radius: 1em !important;
+    }
+
+    .standardContainer{
+        padding-left: 2.4em;
+        padding-right: 2.4em;
+        padding-top: 2em;
+        padding-bottom: 2em;
+    }
+
+}
+
 @media screen and (max-width: 750px) {
     .toolbar {
         position: fixed;
@@ -96,6 +170,26 @@ body{
     .functiontab{
         padding: 0em;
     }
+
+    .ui.grid > .stackable.stackable.row > .column, .ui.stackable.grid > .column.grid > .column, .ui.stackable.grid > .column.row > .column, .ui.stackable.grid > .column:not(.row), .ui.stackable.grid > .row > .column, .ui.stackable.grid > .row > .wide.column, .ui.stackable.grid > .wide.column.serverstatusWrapper {
+        padding: 0rem 0rem !important;
+    }
+
+    #serverstatus.green{
+        border-bottom: 0px solid transparent !important;
+    }
+
+    .greybackground.statustab{
+        border-top-right-radius: 0em !important;
+        padding: 2em 2em !important;
+    }
+
+    .standardContainer{
+        padding-left: 1.2em;
+        padding-right: 1.2em;
+        padding-top: 0.6em;
+        padding-bottom: 0.6em;
+    }
     
 }
 
@@ -127,18 +221,21 @@ body{
     color: white;
 }
 
-.ui.secondary.vertical.menu .active.item .simplistic.icon{
-    color: white;
-}
-
-.simplistic.icon{
-    color: #414141;
+.ui.secondary.vertical.menu .active.item .icon{
+    animation: blinker 3s ease-in-out infinite;
 }
 
 .bluefont{
     color: #a9d1f3 !important;
 }
 
+
+@keyframes blinker {
+    50% {
+        opacity: 50%;
+    }
+}
+
 /*
     Status style overwrite
 */
@@ -157,6 +254,7 @@ body{
 .greybackground.statustab{
     background-color: #414141 !important;
     color: white;
+    
 }
 
 .greybackground.statustab .ui.header:not(:first-child){
@@ -177,8 +275,8 @@ body{
 }
 
 #serverstatus.green{
-    background-color: #f6f6f6 !important;
-    border-bottom: 2px solid #3d9c64;
+    background-color: #fefefe !important;
+    border-right: 5px solid #3d9c64; 
 }
 #serverstatus.green .sub.header{
     color: rgb(224, 224, 224);
@@ -197,7 +295,7 @@ body{
 }
 
 #serverstatus:not(.green){
-    background-color: #f6f6f6 !important;
+    background-color: white !important;
     background-image: url("img/plant.png");
     background-position: right;
     background-repeat: no-repeat;