Przeglądaj źródła

auto update script executed

Toby Chui 1 rok temu
rodzic
commit
771b57b456

+ 1 - 0
api.go

@@ -48,6 +48,7 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/proxy/add", ReverseProxyHandleAddEndpoint)
 	authRouter.HandleFunc("/api/proxy/status", ReverseProxyStatus)
 	authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
+	authRouter.HandleFunc("/api/proxy/vdir", ReverseProxyListVdir)
 	authRouter.HandleFunc("/api/proxy/edit", ReverseProxyHandleEditEndpoint)
 	authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
 	authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)

+ 15 - 2
config.go

@@ -83,6 +83,16 @@ func LoadReverseProxyConfig(configFilepath string) error {
 	return nil
 }
 
+func filterProxyConfigFilename(filename string) string {
+	//Filter out wildcard characters
+	filename = strings.ReplaceAll(filename, "*", "(ST)")
+	filename = strings.ReplaceAll(filename, "?", "(QM)")
+	filename = strings.ReplaceAll(filename, "[", "(OB)")
+	filename = strings.ReplaceAll(filename, "]", "(CB)")
+	filename = strings.ReplaceAll(filename, "#", "(HT)")
+	return filepath.ToSlash(filename)
+}
+
 func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error {
 	//Get filename for saving
 	filename := filepath.Join("./conf/proxy/", endpoint.RootOrMatchingDomain+".config")
@@ -90,7 +100,7 @@ func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error {
 		filename = "./conf/proxy/root.config"
 	}
 
-	filename = filepath.ToSlash(filename)
+	filename = filterProxyConfigFilename(filename)
 
 	//Save config to file
 	js, err := json.MarshalIndent(endpoint, "", " ")
@@ -107,6 +117,9 @@ func RemoveReverseProxyConfig(endpoint string) error {
 	if endpoint == "/" {
 		filename = "./conf/proxy/root.config"
 	}
+
+	filename = filterProxyConfigFilename(filename)
+
 	if !utils.FileExists(filename) {
 		return errors.New("target endpoint not exists")
 	}
@@ -124,7 +137,7 @@ func GetDefaultRootConfig() (*dynamicproxy.ProxyEndpoint, error) {
 		RequireTLS:              false,
 		BypassGlobalTLS:         false,
 		SkipCertValidations:     false,
-		VirtualDirectories:      []*dynamicproxy.ProxyEndpoint{},
+		VirtualDirectories:      []*dynamicproxy.VirtualDirectoryEndpoint{},
 		RequireBasicAuth:        false,
 		BasicAuthCredentials:    []*dynamicproxy.BasicAuthCredentials{},
 		BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},

+ 15 - 29
mod/dynamicproxy/Server.go

@@ -79,38 +79,26 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	/*
-		Subdomain Routing
+		Host Routing
 	*/
-	if strings.Contains(r.Host, ".") {
-		//This might be a subdomain. See if there are any subdomain proxy router for this
-		sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
-		if sep != nil {
-			if sep.RequireBasicAuth {
-				err := h.handleBasicAuthRouting(w, r, sep)
-				if err != nil {
-					return
-				}
+	sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
+	if sep != nil {
+		if sep.RequireBasicAuth {
+			err := h.handleBasicAuthRouting(w, r, sep)
+			if err != nil {
+				return
 			}
-			h.subdomainRequest(w, r, sep)
-			return
 		}
+		h.hostRequest(w, r, sep)
+		return
 	}
 
 	/*
-		Virtual Directory Routing
+		Root Router Handling
 	*/
 	//Clean up the request URI
 	proxyingPath := strings.TrimSpace(r.RequestURI)
-	targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
-	if targetProxyEndpoint != nil {
-		if targetProxyEndpoint.RequireBasicAuth {
-			err := h.handleBasicAuthRouting(w, r, targetProxyEndpoint)
-			if err != nil {
-				return
-			}
-		}
-		h.proxyRequest(w, r, targetProxyEndpoint)
-	} else if !strings.HasSuffix(proxyingPath, "/") {
+	if !strings.HasSuffix(proxyingPath, "/") {
 		potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
 		if potentialProxtEndpoint != nil {
 			//Missing tailing slash. Redirect to target proxy endpoint
@@ -135,6 +123,7 @@ Once entered this routing segment, the root routing options will take over
 for the routing logic.
 */
 func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
+
 	domainOnly := r.Host
 	if strings.Contains(r.Host, ":") {
 		hostPath := strings.Split(r.Host, ":")
@@ -145,10 +134,10 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 	proot := h.Parent.Root
 	switch proot.DefaultSiteOption {
 	case DefaultSite_InternalStaticWebServer:
+		fallthrough
 	case DefaultSite_ReverseProxy:
 		//They both share the same behavior
-		h.proxyRequest(w, r, h.Parent.Root)
-		break
+		h.vdirRequest(w, r, h.Parent.Root)
 	case DefaultSite_Redirect:
 		redirectTarget := strings.TrimSpace(proot.DefaultSiteValue)
 		if redirectTarget == "" {
@@ -159,7 +148,7 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 		parsedURL, err := url.Parse(proot.DefaultSiteValue)
 		if err != nil {
 			//Error when parsing target. Send to root
-			h.proxyRequest(w, r, h.Parent.Root)
+			h.vdirRequest(w, r, h.Parent.Root)
 			return
 		}
 		hostname := parsedURL.Hostname()
@@ -171,11 +160,8 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 
 		h.logRequest(r, false, 307, "root-redirect", domainOnly)
 		http.Redirect(w, r, redirectTarget, http.StatusTemporaryRedirect)
-		break
 	case DefaultSite_NotFoundPage:
 		http.NotFound(w, r)
-		break
-
 	}
 }
 

+ 1 - 0
mod/dynamicproxy/dynamicproxy.go

@@ -299,6 +299,7 @@ func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) {
 	if targetProxyEndpoint == nil {
 		return nil, errors.New("target routing rule not found")
 	}
+
 	return targetProxyEndpoint, nil
 }
 

+ 5 - 9
mod/dynamicproxy/proxyEndpoint.go

@@ -1,7 +1,5 @@
 package dynamicproxy
 
-import "errors"
-
 /*
 	ProxyEndpoint.go
 	author: tobychui
@@ -12,21 +10,19 @@ import "errors"
 	Most of the functions are implemented in dynamicproxy.go
 */
 
-//Update change in the current running proxy endpoint config
+// Update change in the current running proxy endpoint config
 func (ep *ProxyEndpoint) UpdateToRuntime() {
 	ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
 }
 
-//Remove this proxy endpoint from running proxy endpoint list
+// Remove this proxy endpoint from running proxy endpoint list
 func (ep *ProxyEndpoint) Remove() error {
-	//fmt.Println(ptype, key)
 	ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
-	return errors.New("invalid or unsupported type")
-
+	return nil
 }
 
-//ProxyEndpoint remove provide global access by key
-func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
+// ProxyEndpoint remove provide global access by key
+func (router *Router) RemoveProxyEndpointByRootname(rootnameOrMatchingDomain string) error {
 	targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain)
 	if err != nil {
 		return err

+ 19 - 3
mod/dynamicproxy/proxyRequestHandler.go

@@ -6,6 +6,7 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"path/filepath"
 	"strings"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
@@ -35,6 +36,21 @@ func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoi
 		targetSubdomainEndpoint = ep.(*ProxyEndpoint)
 	}
 
+	//No hit. Try with wildcard
+	router.ProxyEndpoints.Range(func(k, v interface{}) bool {
+		ep := v.(*ProxyEndpoint)
+		match, err := filepath.Match(ep.RootOrMatchingDomain, hostname)
+		if err != nil {
+			//Continue
+			return true
+		}
+		if match {
+			targetSubdomainEndpoint = ep
+			return false
+		}
+		return true
+	})
+
 	return targetSubdomainEndpoint
 }
 
@@ -54,8 +70,8 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
 	return rewrittenURL
 }
 
-// Handle subdomain request
-func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
+// Handle host request
+func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
 	r.Header.Set("X-Forwarded-Host", r.Host)
 	r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
 	requestURL := r.URL.String()
@@ -113,7 +129,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
 }
 
 // Handle vdir type request
-func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
+func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
 	rewriteURL := h.Parent.rewriteURL(target.RootOrMatchingDomain, r.RequestURI)
 	r.URL, _ = url.Parse(rewriteURL)
 

+ 16 - 2
mod/dynamicproxy/typedef.go

@@ -16,6 +16,7 @@ import (
 const (
 	ProxyType_Root = 0
 	ProxyType_Host = 1
+	ProxyType_Vdir = 2
 )
 
 type ProxyHandler struct {
@@ -67,10 +68,23 @@ type BasicAuthExceptionRule struct {
 	PathPrefix string
 }
 
+// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
+// program structure than directly using ProxyEndpoint
+type VirtualDirectoryEndpoint struct {
+	MatchingPath string //Matching prefix of the request path, also act as key
+	Domain       string //Domain or IP to proxy to
+
+	RequireTLS          bool //Target domain require TLS
+	BypassGlobalTLS     bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
+	SkipCertValidations bool //Set to true to accept self signed certs
+
+	proxy *dpcore.ReverseProxy `json:"-"`
+}
+
 // A proxy endpoint record, a general interface for handling inbound routing
 type ProxyEndpoint struct {
 	ProxyType            int    //The type of this proxy, see const def
-	RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key
+	RootOrMatchingDomain string //Matching domain for host, also act as key
 	Domain               string //Domain or IP to proxy to
 
 	//TLS/SSL Related
@@ -79,7 +93,7 @@ type ProxyEndpoint struct {
 	SkipCertValidations bool //Set to true to accept self signed certs
 
 	//Virtual Directories
-	VirtualDirectories []*ProxyEndpoint
+	VirtualDirectories []*VirtualDirectoryEndpoint
 
 	//Authentication
 	RequireBasicAuth        bool                      //Set to true to request basic auth before proxy

+ 60 - 13
reverseproxy.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"path/filepath"
 	"sort"
@@ -250,7 +251,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
 			BypassGlobalTLS:     useBypassGlobalTLS,
 			SkipCertValidations: skipTlsValidation,
 			//VDir
-			VirtualDirectories: []*dynamicproxy.ProxyEndpoint{},
+			VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
 			//Auth
 			RequireBasicAuth:        requireBasicAuth,
 			BasicAuthCredentials:    basicAuthCredentials,
@@ -285,9 +286,9 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
 		defaultSiteOption = opt
 
 		dsVal, err := utils.PostPara(r, "defaultSiteVal")
-		if err != nil && (defaultSiteOption == 2 || defaultSiteOption == 3) {
+		if err != nil && (defaultSiteOption == 1 || defaultSiteOption == 2) {
 			//Reverse proxy or redirect, must require value to be set
-			utils.SendErrorResponse(w, "subdomain not defined")
+			utils.SendErrorResponse(w, "target not defined")
 			return
 		}
 
@@ -419,22 +420,21 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	ptype, err := utils.PostPara(r, "ptype")
+	//Remove the config from runtime
+	err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
 	if err != nil {
-		utils.SendErrorResponse(w, "Invalid ptype given")
+		utils.SendErrorResponse(w, err.Error())
 		return
 	}
 
-	//Remove the config from runtime
-	err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
+	//Remove the config from file
+	fmt.Println(ep)
+	err = RemoveReverseProxyConfig(ep)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
 
-	//Remove the config from file
-	RemoveReverseProxyConfig(ep)
-
 	//Update utm if exists
 	if uptimeMonitor != nil {
 		uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
@@ -692,9 +692,18 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
 	if eptype == "host" {
 		results := []*dynamicproxy.ProxyEndpoint{}
 		dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
-			thisEndpoint := value.(*dynamicproxy.ProxyEndpoint)
-			//Clear the auth credentials before showing to front-end
-			thisEndpoint.BasicAuthCredentials = []*dynamicproxy.BasicAuthCredentials{}
+			thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
+
+			//Clear the auth passwords before showing to front-end
+			cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
+			for _, user := range thisEndpoint.BasicAuthCredentials {
+				cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{
+					Username:     user.Username,
+					PasswordHash: "",
+				})
+			}
+
+			thisEndpoint.BasicAuthCredentials = cleanedCredentials
 			results = append(results, thisEndpoint)
 			return true
 		})
@@ -713,6 +722,44 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+// List the Virtual directory under given proxy rule
+func ReverseProxyListVdir(w http.ResponseWriter, r *http.Request) {
+	eptype, err := utils.PostPara(r, "type") //Support root and host
+	if err != nil {
+		utils.SendErrorResponse(w, "type not defined")
+		return
+	}
+
+	var targetEndpoint *dynamicproxy.ProxyEndpoint
+	if eptype == "host" {
+		endpoint, err := utils.PostPara(r, "ep") //Support root and host
+		if err != nil {
+			utils.SendErrorResponse(w, "endpoint not defined")
+			return
+		}
+
+		targetEndpoint, err = dynamicProxyRouter.LoadProxy(endpoint)
+		if err != nil {
+			utils.SendErrorResponse(w, "target endpoint not found")
+			return
+		}
+	} else if eptype == "root" {
+		targetEndpoint = dynamicProxyRouter.Root
+	} else {
+		utils.SendErrorResponse(w, "invalid type given")
+		return
+	}
+
+	//Parse result to json
+	vdirs := targetEndpoint.VirtualDirectories
+	if targetEndpoint.VirtualDirectories == nil {
+		//Avoid returning null to front-end
+		vdirs = []*dynamicproxy.VirtualDirectoryEndpoint{}
+	}
+	js, _ := json.Marshal(vdirs)
+	utils.SendJSONResponse(w, string(js))
+}
+
 // Handle port 80 incoming traffics
 func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
 	enabled, err := utils.GetPara(r, "enable")

+ 21 - 19
web/components/subd.html → web/components/httprp.html

@@ -1,38 +1,39 @@
 <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>
+        <h2>HTTP Proxy</h2>
+        <p>Proxy HTTP server with HTTP or HTTPS for multiple hosts. If you are only proxying for one host / domain, use Default Site instead.</p>
     </div>
-    <div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
-        <table class="ui celled sortable unstackable compact table">
+    <div style="width: 100%; overflow-x: auto; margin-bottom: 1em; min-width: 400px;">
+        <table class="ui celled sortable unstackable basic compact table">
             <thead>
                 <tr>
-                    <th>Matching Domain</th>
-                    <th>Proxy To</th>
+                    <th>Host</th>
+                    <th>Destination</th>
+                    <th>Virtual Directory</th>
                     <th>Basic Auth</th>
                     <th class="no-sort" style="min-width: 7.2em;">Actions</th>
                 </tr>
             </thead>
-            <tbody id="subdList">
+            <tbody id="httpProxyList">
             
             </tbody>
         </table>
     </div>
-    <button class="ui icon right floated basic button" onclick="listSubd();"><i class="green refresh icon"></i> Refresh</button>
+    <button class="ui icon right floated basic button" onclick="listProxyEndpoints();"><i class="green refresh icon"></i> Refresh</button>
     <br><br>
 </div>
 
 <script>
-    function listSubd(){
-        $.get("/api/proxy/list?type=subd", function(data){
-            $("#subdList").html(``);
+    function listProxyEndpoints(){
+        $.get("/api/proxy/list?type=host", function(data){
+            $("#httpProxyList").html(``);
             if (data.error !== undefined){
-                    $("#subdList").append(`<tr>
+                    $("#httpProxyList").append(`<tr>
                     <td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
                 </tr>`);
             }else if (data.length == 0){
-                $("#subdList").append(`<tr>
-                    <td data-label="" colspan="3"><i class="checkmark icon"></i> No Subdomain Proxy Record</td>
+                $("#httpProxyList").append(`<tr>
+                    <td data-label="" colspan="3"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
                 </tr>`);
             }else{
                 data.forEach(subd => {
@@ -56,13 +57,14 @@
                     }
                     
 
-                    $("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
+                    $("#httpProxyList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
                         <td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}</td>
                         <td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
+                        <td data-label="" editable="true" datatype="vdir">WIP</td>
                         <td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
                         <td class="center aligned" editable="true" datatype="action" data-label="">
-                            <button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
-                            <button class="ui circular mini red basic icon button" onclick='deleteEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
+                            <button class="ui circular mini basic icon button editBtn inlineEditActionBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
+                            <button class="ui circular mini red basic icon button inlineEditActionBtn" onclick='deleteEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
                         </td>
                     </tr>`);
                 });
@@ -71,7 +73,7 @@
     }   
 
     //Bind on tab switch events
-    tabSwitchEventBind["subd"] = function(){
-        listSubd();
+    tabSwitchEventBind["httprp"] = function(){
+        listProxyEndpoints();
     }
 </script>

+ 39 - 34
web/components/rproot.html

@@ -63,20 +63,20 @@
                 <div class="field">
                     <label>Redirect target domain</label>
                     <div class="ui input">
-                        <input id="unsetRedirectDomain" type="text" placeholder="http://example.com">
+                        <input id="redirectDomain" type="text" placeholder="http://example.com">
                     </div>
                     <small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)</small>
                 </div>
             </div>
         </div>
 
-        <button class="ui basic button" onclick="setProxyRoot()"><i class="green checkmark icon" ></i> Apply Changes</button>
+        <button class="ui basic button" onclick="setProxyRoot(this)"><i class="green checkmark icon" ></i> Apply Changes</button>
         <button class="ui basic button" onclick="initRootInfo()"><i class="refresh icon" ></i> Reset</button>
         <br>
     </div>
 </div>
 <script>
-    let currentDefaultSiteOption = "webserver";
+    var currentDefaultSiteOption = 0; //For enum see typedef.go
     $("#advanceRootSettings").accordion();
 
     //Handle toggle events of option radio boxes
@@ -91,22 +91,22 @@
             $("#proxyRoot").parent().addClass("disabled");
             $("#rootReqTLS").parent().checkbox("set unchecked");
             $("#rootReqTLS").parent().addClass("disabled");
-
+            currentDefaultSiteOption = 0;
         }else if (selectedDefaultSite == "proxy"){
             $("#defaultSiteProxyOptions").show();
             $("#rootReqTLS").parent().removeClass("disabled");
             $("#proxyRoot").parent().removeClass("disabled");
-            //initRootInfo();
+            currentDefaultSiteOption = 1;
         }else if (selectedDefaultSite == "redirect"){
             $("#defaultSiteRedirectOptions").show();
+            currentDefaultSiteOption = 2;
         }else if (selectedDefaultSite == "notfound"){
-
+            currentDefaultSiteOption = 3;
         }else{
             //Unknown option
             return;
         }
 
-        currentDefaultSiteOption = selectedDefaultSite;
     }
 
     //Bind events to the radio boxes
@@ -122,14 +122,16 @@
 
             }else{
                 var $radios = $('input:radio[name=defaultsiteOption]');
-                let proxyType = data.ProxyType;
+                let proxyType = data.DefaultSiteOption;
                 //See typedef.go for enum conversion
                 if (proxyType == 0){
                     $radios.filter('[value=webserver]').prop('checked', true);
                 }else if (proxyType == 1){
                     $radios.filter('[value=proxy]').prop('checked', true);
+                    $("#proxyRoot").val(data.DefaultSiteValue);
                 }else if (proxyType == 2){
                     $radios.filter('[value=redirect]').prop('checked', true);
+                    $("#redirectDomain").val(data.DefaultSiteValue);
                 }else if (proxyType == 3){
                     $radios.filter('[value=notfound]').prop('checked', true);
                 }
@@ -145,25 +147,9 @@
         });
     }
     initRootInfo(function(){
-        updateWebServerLinkSettings(function(){
-            bindDefaultSiteRadioCheckboxEvents();
-        });
+        bindDefaultSiteRadioCheckboxEvents();
     });
 
-    //Update the current web server port settings
-    function updateWebServerLinkSettings(callback=undefined){
-        isUsingStaticWebServerAsRoot(function(isUsingWebServ){
-            if (isUsingWebServ){
-                //Select the 
-            }else{
-                
-            }
-
-            if (callback != undefined){
-                callback(isUsingWebServ);
-            }
-        })
-    }
 
     function isUsingStaticWebServerAsRoot(callback){
         let currentProxyRoot = $("#proxyRoot").val().trim();
@@ -208,7 +194,10 @@
     }
 
     //Set the new proxy root option
-    function setProxyRoot(){
+    function setProxyRoot(btn=undefined){
+        if (btn != undefined){
+            $(btn).addClass("disabled");
+        }
         var newpr = $("#proxyRoot").val();
         if (newpr.trim() == ""){
             $("#proxyRoot").parent().addClass('error');
@@ -219,11 +208,24 @@
 
         var rootReqTls = $("#rootReqTLS")[0].checked;
 
-        //Convert the default site option to enum
-        var defaultSiteOpt = 0;
-        
-        
+        //proxy mode or redirect mode, check for input values
+        var defaultSiteValue = "";
+        if (currentDefaultSiteOption == 1){
+            if ($("#proxyRoot").val().trim() == ""){
+                $("#proxyRoot").parent().addClass("error");
+                return;
+            }
+            defaultSiteValue = $("#proxyRoot").val().trim();
+            $("#proxyRoot").parent().removeClass("error");
 
+        }else if (currentDefaultSiteOption == 2){
+            if ($("#redirectDomain").val().trim() == ""){
+                $("#redirectDomain").parent().addClass("error");
+                return;
+            }
+            defaultSiteValue = $("#redirectDomain").val().trim();
+            $("#redirectDomain").parent().removeClass("error");
+        }
 
         //Create the endpoint by calling add
         $.ajax({
@@ -232,8 +234,8 @@
                 "type": "root", 
                 "tls": rootReqTls, 
                 "ep": newpr,
-                "defaultSiteOpt": "",
-                "defaultSiteVal":"",
+                "defaultSiteOpt": currentDefaultSiteOption,
+                "defaultSiteVal":defaultSiteValue,
             },
             method: "POST",
             success: function(data){
@@ -252,14 +254,17 @@
                             
                             setTimeout(function(){
                                  //Update the checkbox
-                                updateWebServerLinkSettings();
                                 msgbox("Proxy Root Updated");
-                            }, 1000);
+                            }, 100);
                             
                         })
                     });
                     
                 }
+
+                if (btn != undefined){
+                    $(btn).removeClass("disabled");
+                }
             }
         });
 

+ 104 - 153
web/components/rules.html

@@ -1,119 +1,114 @@
-<div class="ui stackable grid">
+<!-- Proxy Create Form-->
+<style>
+    .rulesInstructions{
+        background: var(--theme_background) !important; 
+        color: var(--theme_lgrey);
+        border-radius: 1em !important;
+    }
+</style>
+<div class="standardContainer">
+    <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" onchange="handleProxyTypeOptionChange(this.value)">
-                                <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>Target 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 class="ui basic segment" style="border-radius: 1em; padding: 1em !important;">
+                <h2>New Proxy Rule</h2>
+                <p>You can add more proxy rules to support more site via domain / subdomains</p>
+                <div class="ui form">
+                    <div class="field">
+                        <label>Matching Keyword / Domain</label>
+                        <input type="text" id="rootname" placeholder="mydomain.com">
+                        <small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com</small>
+                    </div>
+                    <div class="field">
+                        <label>Target 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 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>
+                    <!-- Advance configs -->
+                    <div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
+                        <div id="advanceProxyRules" class="ui fluid accordion">
+                            <div class="title">
+                            <i class="dropdown icon"></i>
+                            Advance Settings
                             </div>
-                        </div>
-                        <!-- Advance configs -->
-                        <div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
-                            <div id="advanceProxyRules" class="ui fluid accordion">
-                                <div class="title">
-                                <i class="dropdown icon"></i>
-                                Advance Settings
+                            <div class="content">
+                                <p></p>
+                                <div class="field">
+                                    <div class="ui checkbox">
+                                        <input type="checkbox" id="skipTLSValidation">
+                                        <label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
+                                    </div>
                                 </div>
-                                <div class="content">
-                                    <p></p>
-                                    <div class="field">
-                                        <div class="ui checkbox">
-                                            <input type="checkbox" id="skipTLSValidation">
-                                            <label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
-                                        </div>
+                                <div class="field">
+                                    <div class="ui checkbox">
+                                        <input type="checkbox" id="bypassGlobalTLS">
+                                        <label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
                                     </div>
-                                    <div class="field">
-                                        <div class="ui checkbox">
-                                            <input type="checkbox" id="bypassGlobalTLS">
-                                            <label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
-                                        </div>
+                                </div>
+                                <div class="field">
+                                    <div class="ui checkbox">
+                                        <input type="checkbox" id="requireBasicAuth">
+                                        <label>Require Basic Auth<br><small>Require client to login in order to view the page</small></label>
                                     </div>
-                                    <div class="field">
-                                        <div class="ui checkbox">
-                                            <input type="checkbox" id="requireBasicAuth">
-                                            <label>Require Basic Auth<br><small>Require client to login in order to view the page</small></label>
+                                </div>
+                                <div id="basicAuthCredentials" class="field">
+                                    <p>Enter the username and password for allowing them to access this proxy endpoint</p>
+                                    <table class="ui very basic celled table">
+                                        <thead>
+                                        <tr>
+                                            <th>Username</th>
+                                            <th>Password</th>
+                                            <th>Remove</th>
+                                        </tr></thead>
+                                        <tbody id="basicAuthCredentialTable">
+                                        <tr>
+                                            <td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td>
+                                        </tr>
+                                        </tbody>
+                                    </table>
+                                    <div class="three small fields credentialEntry">
+                                        <div class="field">
+                                            <input id="basicAuthCredUsername" type="text" placeholder="Username" autocomplete="off">
                                         </div>
-                                    </div>
-                                    <div id="basicAuthCredentials" class="field">
-                                        <p>Enter the username and password for allowing them to access this proxy endpoint</p>
-                                        <table class="ui very basic celled table">
-                                            <thead>
-                                            <tr>
-                                                <th>Username</th>
-                                                <th>Password</th>
-                                                <th>Remove</th>
-                                            </tr></thead>
-                                            <tbody id="basicAuthCredentialTable">
-                                            <tr>
-                                                <td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td>
-                                            </tr>
-                                            </tbody>
-                                        </table>
-                                        <div class="three small fields credentialEntry">
-                                            <div class="field">
-                                                <input id="basicAuthCredUsername" type="text" placeholder="Username" autocomplete="off">
-                                            </div>
-                                            <div class="field">
-                                                <input id="basicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
-                                            </div>
-                                            <div class="field">
-                                                <button class="ui basic button" onclick="addCredentials();"><i class="blue add icon"></i> Add Credential</button>
-                                            </div>
+                                        <div class="field">
+                                            <input id="basicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
+                                        </div>
+                                        <div class="field">
+                                            <button class="ui basic button" onclick="addCredentials();"><i class="blue add icon"></i> Add Credential</button>
                                         </div>
                                     </div>
                                 </div>
                             </div>
                         </div>
-                        <br>
-                        <button class="ui basic button" onclick="newProxyEndpoint();"><i class="blue add icon"></i> Create Endpoint</button>
-                        <br><br>
                     </div>
+                    <br>
+                    <button class="ui basic button" onclick="newProxyEndpoint();"><i class="blue add icon"></i> Create Endpoint</button>
+                    <br><br>
                 </div>
             </div>
         </div>
         <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>
+            <div class="ui basic segment rulesInstructions">
+                <span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Domain</span><br>
+                Example of domain matching keyword:<br>
+                <code>arozos.com</code> <br>Any acess requesting 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;"><i class="ui yellow star icon"></i> 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>
+                <code>s1.arozos.com</code> <br>Any request 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/home/ will be proxy to the IP address below)<br>
-                You can also ignore the tailing slash for wildcard like usage.<br>
-                <code>/s1/room-</code> <br>Any access to {this_server}/s1/classroom_* will be proxied, for example: <br>
+                <span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Wildcard</span><br>
+                Example of wildcard matching keyword:<br>
+                <code>*.arozos.com</code> <br>Any request with a host name matching *.arozos.com will be proxy to the IP address below. Here are some examples.<br>
                 <div class="ui list">
-                    <div class="item"><code>/s1/room-101</code></div>
-                    <div class="item"><code>/s1/room-102/</code></div>
-                    <div class="item"><code>/s1/room-103/map.txt</code></div>
-                </div><br>
-                
+                    <div class="item"><code>www.arozos.com</code></div>
+                    <div class="item"><code>foo.bar.arozos.com</code></div>
+                </div>
                 <br>
             </div>
         </div>
@@ -124,7 +119,6 @@
 
     //New Proxy Endpoint
     function newProxyEndpoint(){
-        var type = $("#ptype").val();
         var rootname = $("#rootname").val();
         var proxyDomain = $("#proxyDomain").val();
         var useTLS = $("#reqTls")[0].checked;
@@ -132,20 +126,6 @@
         var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
         var requireBasicAuth = $("#requireBasicAuth")[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
@@ -164,7 +144,7 @@
         $.ajax({
             url: "/api/proxy/add",
             data: {
-                type: type, 
+                type: "host",
                 rootname: rootname, 
                 tls: useTLS, 
                 ep: proxyDomain,
@@ -178,8 +158,6 @@
                     msgbox(data.error, false, 5000);
                 }else{
                     //OK
-                    listVdirs();
-                    listSubd();
                    
 
                     //Clear old data
@@ -189,7 +167,7 @@
                     updateTable();
 
                     //Check if it is a new subdomain and TLS enabled
-                    if (type == "subd" && $("#tls").checkbox("is checked")){
+                    if ($("#tls").checkbox("is checked")){
                         confirmBox("Request new SSL Cert for this subdomain?", function(choice){
                             if (choice == true){
                                 //Load the prefer CA from TLS page
@@ -214,23 +192,14 @@
         
     }
 
-    function handleProxyTypeOptionChange(newType){
-        if (newType == "subd"){
-            $("#bypassGlobalTLS").parent().removeClass("disabled");
-        }else if (newType == "vdir"){
-            $("#bypassGlobalTLS").parent().addClass("disabled");
-        }
-    }
-
     //Generic functions for delete rp endpoints 
     function deleteEndpoint(ptype, epoint){
         if (confirm("Confirm remove proxy for :" + epoint + "?")){
             $.ajax({
                 url: "/api/proxy/del",
-                data: {ep: epoint},
+                data: {ep: epoint, },
                 success: function(){
-                    listVdirs();
-                    listSubd();
+                    listProxyEndpoints();
                 }
             })
         }
@@ -330,16 +299,8 @@
         updateTable();
     }
 
-
-    //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);
-    } 
-
-
     /*
-        Inline editor for subd.html and vdir.html
+        Inline editor for httprp.html
     */
 
     function editEndpoint(endpointType, uuid) {
@@ -408,10 +369,11 @@
 
             }else if (datatype == 'action'){
                 column.empty().append(`
-                <button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
-                <button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
+                <button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small icon circular button inlineEditActionBtn"><i class="ui green save icon"></i></button>
+                <button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small icon circular button inlineEditActionBtn"><i class="ui remove icon"></i></button>
+                
                 `);
-            }else if (datatype == "inbound" && payload.ProxyType == 0){
+            }else if (datatype == "inbound"){
                 let originalContent = $(column).html();
                 column.empty().append(`${originalContent}
                     <div class="ui divider"></div>
@@ -430,8 +392,7 @@
     }
 
     function exitProxyInlineEdit(endpointType){
-        listSubd();
-        listVdirs();
+        listProxyEndpoints();
         $("#" + endpointType).find(".editBtn").removeClass("disabled");
     }
 
@@ -440,14 +401,8 @@
         if (row.length == 0){
             return;
         }
-
-        var epttype = $(row).attr("class");
-        if (epttype == "subdEntry"){
-            epttype = "subd";
-        }else if (epttype == "vdirEntry"){
-            epttype = "vdir";
-        }
-
+        
+        var epttype = "host";
         let newDomain =  $(row).find(".Domain").val();
         let requireTLS = $(row).find(".RequireTLS")[0].checked;
         let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
@@ -473,11 +428,7 @@
                     msgbox(data.error, false, 6000);
                 }else{
                     msgbox("Proxy endpoint updated");
-                    if (epttype == "subd"){
-                        listSubd();
-                    }else if (epttype == "vdir"){
-                        listVdirs();
-                    }
+                    listProxyEndpoints();
                 }
             }
         })

+ 98 - 18
web/components/vdir.html

@@ -3,28 +3,107 @@
         <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>
-    <div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
-        <table class="ui celled sortable unstackable compact table">
-            <thead>
-                <tr>
-                    <th>Virtual Directory</th>
-                    <th>Proxy To</th>
-                    <th>Basic Auth</th>
-                    <th class="no-sort" style="min-width: 7.2em;">Actions</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>
+    <div class="ui stackable grid">
+        <div class="six wide column">
+            <h4>Select a Target Host / Site</h4>
+            <p>Attach Virtual Directory routing rule to root proxy router</p>
+            <div class="ui checkbox">
+                <input type="checkbox" id="useRootProxyRouterForVdir" onchange="handleVdirAttachTargetChange(this);">
+                <label>Use Root Proxy Router<br>
+                <small>Only applicable when Default Site is set to "Reverse Proxy" mode</small></label>
+            </div>
+            <div class="ui horizontal divider">OR</div>
+            <p>Create Virtual Directory routing in existing host / routing rule entries</p>
+            <div class="ui fluid search selection dropdown">
+                <input type="hidden" id="vdirBaseRoutingRule" name="vdirBaseRoutingRule" onchange="handleVdirAttachTargetChange();">
+                <i class="dropdown icon"></i>
+                <div class="default text">Select a host to edit</div>
+                <div class="menu" id="hostDomainList">
+                    
+                </div>
+            </div>
+        </div>
+        <div class="ten wide column">
+            <h4>Virtual Directory Routing Rules</h4>
+            <p>The following are the list of Virtual Directories currently handled by the host router above</p>
+            <div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
+                <table class="ui celled sortable basic unstackable compact table">
+                    <thead>
+                        <tr>
+                            <th>Virtual Directory</th>
+                            <th>Proxy To</th>
+                            <th>Basic Auth</th>
+                            <th class="no-sort" style="min-width: 7.2em;">Actions</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>
+            </div>
+            <button class="ui icon right floated basic button" onclick="listVdirs();"><i class="green refresh icon"></i> Refresh</button>
+        </div>
     </div>
-    <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
+
+    //Initialize the list of hosts that can be used to attach vdirs config
+    function initVdirList(){
+        //Load the hosts into the dropdown
+        $("#hostDomainList").html("");
+        $.get("/api/proxy/list?type=host", function(data){
+            if (data.error != undefined){
+                msgbox(data.error, false);
+            }else{
+                if (data.length == 0){
+                    //No hosts found
+                }else{
+                    data.forEach(proxyEndpoint => {
+                        let domain = proxyEndpoint.RootOrMatchingDomain;
+                        $("#hostDomainList").append(`<div class="item" data-value="${domain}">${domain}</div>`);
+                    });
+
+                    //Update the dropdown events
+                    $("#vdirBaseRoutingRule").parent().dropdown();
+                }
+            }
+        });
+    }
+
+    function handleVdirAttachTargetChange(targetCheckbox=undefined){
+        if (targetCheckbox != undefined && targetCheckbox.checked){
+            $("#vdirBaseRoutingRule").parent().addClass("disabled");
+
+            //Load the vdir list for root
+            loadVdirList("root");
+        }else{
+            $("#vdirBaseRoutingRule").parent().removeClass("disabled");
+            let selectedEndpointRule = $("#vdirBaseRoutingRule").val();
+            if (selectedEndpointRule != ""){
+                loadVdirList(selectedEndpointRule);
+            }
+        }
+    }
+
+    function loadVdirList(endpoint){
+        alert("Loading endpoint " + endpoint)
+        if (endpoint == "root"){
+            //Load root endpoint vdir list
+
+        }else{
+            //Load target endpoint list
+
+        }
+    }
+
+    /*
     function listVdirs(){
         $.get("/api/proxy/list?type=vdir", function(data){
             $("#vdirList").html(``);
@@ -67,9 +146,10 @@
             }
         });
     }
+    */
 
     //Bind on tab switch events
     tabSwitchEventBind["vdir"] = function(){
-        listVdirs();
+        initVdirList();
     }
 </script>

+ 6 - 5
web/index.html

@@ -40,18 +40,19 @@
                         <i class="simplistic home icon"></i> Default Site
                     </a>
                     <div class="ui divider menudivider">Reverse Proxy</div>
-                    <a class="item" tag="subd">
+                    <a class="item" tag="httprp">
                         <i class="simplistic sitemap icon"></i> HTTP Proxy
                     </a>
-                    <a class="item" tag="tcpprox">
-                        <i class="simplistic exchange icon"></i> TCP Proxy
-                    </a>
                     <a class="item" tag="vdir">
                         <i class="simplistic folder icon"></i> Virtual Directory
                     </a>
                     <a class="item" tag="rules">
                         <i class="simplistic plus square icon"></i> Create Proxy Rules
                     </a>
+                    <a class="item" tag="tcpprox">
+                        <i class="simplistic exchange icon"></i> TCP Proxy
+                    </a>
+                   
                    
                     <div class="ui divider menudivider">Access & Connections</div>
                     <a class="item" tag="cert">
@@ -100,7 +101,7 @@
                 <div id="vdir" class="functiontab" target="vdir.html"></div>
 
                 <!-- Subdomain Proxy -->
-                <div id="subd" class="functiontab" target="subd.html"></div>
+                <div id="httprp" class="functiontab" target="httprp.html"></div>
 
                 <!-- Create Rules -->
                 <div id="rules" class="functiontab" target="rules.html"></div>

+ 10 - 0
web/main.css

@@ -304,6 +304,10 @@ body{
     color: #5e5d5d;
 }
 
+.ui.segment{
+    box-shadow: none !important;
+}
+
 .ui.secondary.vertical.menu .active.item{
     background: var(--theme_background);
     font-weight: 600;
@@ -460,6 +464,12 @@ body{
     }
   }
 
+/*
+  HTTP Proxy overwrite
+*/
+
+
+
 /*
     Access Control
 */

+ 7 - 3
web/snippet/basicAuthEditor.html

@@ -41,8 +41,7 @@
                             <input id="inlineEditBasicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
                         </div>
                         <div class="field" >
-                            <button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
-                            <button class="ui basic button"  style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save Credential</button>
+                            <button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="green add icon"></i> Add Credential</button>
                         </div>
                         <div class="ui divider"></div>
                     </div>
@@ -69,7 +68,7 @@
                     <small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
                 </div>
                 <div class="field" >
-                    <button class="ui basic button" onclick="addExceptionPath();"><i class="blue add icon"></i> Add Exception</button>
+                    <button class="ui basic button" onclick="addExceptionPath();"><i class="yellow add icon"></i> Add Exception</button>
                 </div>
                 <div class="field">
                     <div class="ui basic message">
@@ -162,6 +161,9 @@
 
                 // Update the table body with the credentials
                 updateEditingCredentialList();
+
+                //Save the table
+                saveCredentials();
             }
 
             function addExceptionPath(){
@@ -268,6 +270,8 @@
 
                 // Update the table body
                 updateEditingCredentialList();
+                
+                saveCredentials();
             }
 
             function alreadyExists(username){