Przeglądaj źródła

auto update script executed

Toby Chui 1 rok temu
rodzic
commit
da9f659d66

+ 77 - 67
config.go

@@ -3,6 +3,7 @@ package main
 import (
 	"archive/zip"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -35,97 +36,106 @@ type Record struct {
 	BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
 }
 
-// Save a reverse proxy config record to file
-func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error {
-	//TODO: Make this accept new def types
-	os.MkdirAll("./conf/proxy/", 0775)
-	filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
-
-	//Generate record
-	thisRecord := proxyConfigRecord
-
-	//Write to file
-	js, _ := json.MarshalIndent(thisRecord, "", " ")
-	return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
-}
+/*
+Load Reverse Proxy Config from file and append it to current runtime proxy router
+*/
+func LoadReverseProxyConfig(configFilepath string) error {
+	//Load the config file from disk
+	endpointConfig, err := os.ReadFile(configFilepath)
+	if err != nil {
+		return err
+	}
 
-// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion)
-func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error {
-	recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint)
+	//Parse it into dynamic proxy endpoint
+	thisConfigEndpoint := dynamicproxy.ProxyEndpoint{}
+	err = json.Unmarshal(endpointConfig, &thisConfigEndpoint)
 	if err != nil {
 		return err
 	}
-	return SaveReverseProxyConfigToFile(recordToSave)
-}
 
-func RemoveReverseProxyConfigFile(rootname string) error {
-	filename := getFilenameFromRootName(rootname)
-	removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
-	SystemWideLogger.Println("Config Removed: ", removePendingFile)
-	if utils.FileExists(removePendingFile) {
-		err := os.Remove(removePendingFile)
+	//Matching domain not set. Assume root
+	if thisConfigEndpoint.RootOrMatchingDomain == "" {
+		thisConfigEndpoint.RootOrMatchingDomain = "/"
+	}
+
+	if thisConfigEndpoint.ProxyType == dynamicproxy.ProxyType_Root {
+		//This is a root config file
+		rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisConfigEndpoint)
 		if err != nil {
-			SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err)
 			return err
 		}
+
+		dynamicProxyRouter.SetProxyRouteAsRoot(rootProxyEndpoint)
+
+	} else if thisConfigEndpoint.ProxyType == dynamicproxy.ProxyType_Host {
+		//This is a host config file
+		readyProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisConfigEndpoint)
+		if err != nil {
+			return err
+		}
+
+		dynamicProxyRouter.AddProxyRouteToRuntime(readyProxyEndpoint)
+	} else {
+		return errors.New("not supported proxy type")
 	}
 
-	//File already gone
+	SystemWideLogger.PrintAndLog("Proxy", thisConfigEndpoint.RootOrMatchingDomain+" -> "+thisConfigEndpoint.Domain+" routing rule loaded", nil)
 	return nil
 }
 
-// Return ptype, rootname and proxyTarget, error if any
-func LoadReverseProxyConfig(filename string) (*Record, error) {
-	thisRecord := Record{
-		ProxyType:               "",
-		Rootname:                "",
-		ProxyTarget:             "",
-		UseTLS:                  false,
-		BypassGlobalTLS:         false,
-		SkipTlsValidation:       false,
-		RequireBasicAuth:        false,
-		BasicAuthCredentials:    []*dynamicproxy.BasicAuthCredentials{},
-		BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
+func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error {
+	//Get filename for saving
+	filename := filepath.Join("./conf/proxy/", endpoint.RootOrMatchingDomain+".config")
+	if endpoint.ProxyType == dynamicproxy.ProxyType_Root {
+		filename = "./conf/proxy/root.config"
 	}
 
-	configContent, err := os.ReadFile(filename)
-	if err != nil {
-		return &thisRecord, err
-	}
+	filename = filepath.ToSlash(filename)
 
-	//Unmarshal the content into config
-	err = json.Unmarshal(configContent, &thisRecord)
+	//Save config to file
+	js, err := json.MarshalIndent(endpoint, "", " ")
 	if err != nil {
-		return &thisRecord, err
+		return err
 	}
 
-	//Return it
-	return &thisRecord, nil
+	os.WriteFile(filename, js, 0775)
+	return nil
 }
 
-// Convert a running proxy endpoint object into a save-able record struct
-func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoint) (*Record, error) {
-	thisProxyConfigRecord := Record{
-		ProxyType:               targetProxyEndpoint.GetProxyTypeString(),
-		Rootname:                targetProxyEndpoint.RootOrMatchingDomain,
-		ProxyTarget:             targetProxyEndpoint.Domain,
-		UseTLS:                  targetProxyEndpoint.RequireTLS,
-		BypassGlobalTLS:         targetProxyEndpoint.BypassGlobalTLS,
-		SkipTlsValidation:       targetProxyEndpoint.SkipCertValidations,
-		RequireBasicAuth:        targetProxyEndpoint.RequireBasicAuth,
-		BasicAuthCredentials:    targetProxyEndpoint.BasicAuthCredentials,
-		BasicAuthExceptionRules: targetProxyEndpoint.BasicAuthExceptionRules,
+func RemoveReverseProxyConfig(endpoint string) error {
+	filename := filepath.Join("./conf/proxy/", endpoint+".config")
+	if endpoint == "/" {
+		filename = "./conf/proxy/root.config"
 	}
-
-	return &thisProxyConfigRecord, nil
+	if !utils.FileExists(filename) {
+		return errors.New("target endpoint not exists")
+	}
+	return os.Remove(filename)
 }
 
-func getFilenameFromRootName(rootname string) string {
-	//Generate a filename for this rootname
-	filename := strings.ReplaceAll(rootname, ".", "_")
-	filename = strings.ReplaceAll(filename, "/", "-")
-	filename = filename + ".config"
-	return filename
+// Get the default root config that point to the internal static web server
+// this will be used if root config is not found (new deployment / missing root.config file)
+func GetDefaultRootConfig() (*dynamicproxy.ProxyEndpoint, error) {
+	//Default settings
+	rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&dynamicproxy.ProxyEndpoint{
+		ProxyType:               dynamicproxy.ProxyType_Root,
+		RootOrMatchingDomain:    "/",
+		Domain:                  "127.0.0.1:" + staticWebServer.GetListeningPort(),
+		RequireTLS:              false,
+		BypassGlobalTLS:         false,
+		SkipCertValidations:     false,
+		VirtualDirectories:      []*dynamicproxy.ProxyEndpoint{},
+		RequireBasicAuth:        false,
+		BasicAuthCredentials:    []*dynamicproxy.BasicAuthCredentials{},
+		BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
+		DefaultSiteOption:       dynamicproxy.DefaultSite_InternalStaticWebServer,
+		DefaultSiteValue:        "",
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return rootProxyEndpoint, nil
 }
 
 /*

+ 34 - 40
mod/dynamicproxy/Server.go

@@ -3,7 +3,6 @@ package dynamicproxy
 import (
 	_ "embed"
 	"errors"
-	"log"
 	"net/http"
 	"net/url"
 	"os"
@@ -84,7 +83,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	*/
 	if strings.Contains(r.Host, ".") {
 		//This might be a subdomain. See if there are any subdomain proxy router for this
-		sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
+		sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
 		if sep != nil {
 			if sep.RequireBasicAuth {
 				err := h.handleBasicAuthRouting(w, r, sep)
@@ -142,46 +141,41 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 		domainOnly = hostPath[0]
 	}
 
-	if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
-		//Route to custom domain
-		if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
-			//Not set. Redirect to first level of domain redirectable
-			fld, err := h.getTopLevelRedirectableDomain(domainOnly)
-			if err != nil {
-				//Redirect to proxy root
-				h.proxyRequest(w, r, h.Parent.Root)
-			} else {
-				log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
-				h.logRequest(r, false, 307, "root-redirect", domainOnly)
-				http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
-			}
-			return
-		} else if h.isTopLevelRedirectableDomain(domainOnly) {
-			//This is requesting a top level private domain that should be serving root
+	//Get the proxy root config
+	proot := h.Parent.Root
+	switch proot.DefaultSiteOption {
+	case DefaultSite_InternalStaticWebServer:
+	case DefaultSite_ReverseProxy:
+		//They both share the same behavior
+		h.proxyRequest(w, r, h.Parent.Root)
+		break
+	case DefaultSite_Redirect:
+		redirectTarget := strings.TrimSpace(proot.DefaultSiteValue)
+		if redirectTarget == "" {
+			redirectTarget = "about:blank"
+		}
+
+		//Check if it is an infinite loopback redirect
+		parsedURL, err := url.Parse(proot.DefaultSiteValue)
+		if err != nil {
+			//Error when parsing target. Send to root
 			h.proxyRequest(w, r, h.Parent.Root)
-		} else {
-			//Validate the redirection target URL
-			parsedURL, err := url.Parse(h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget)
-			if err != nil {
-				//Error when parsing target. Send to root
-				h.proxyRequest(w, r, h.Parent.Root)
-				return
-			}
-			hostname := parsedURL.Hostname()
-			if domainOnly != hostname {
-				//Redirect to target
-				h.logRequest(r, false, 307, "root-redirect", domainOnly)
-				http.Redirect(w, r, h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget, http.StatusTemporaryRedirect)
-				return
-			} else {
-				//Loopback request due to bad settings (Shd leave it empty)
-				//Forward it to root proxy
-				h.proxyRequest(w, r, h.Parent.Root)
-			}
+			return
 		}
-	} else {
-		//Route to root
-		h.proxyRequest(w, r, h.Parent.Root)
+		hostname := parsedURL.Hostname()
+		if hostname == domainOnly {
+			h.logRequest(r, false, 500, "root-redirect", domainOnly)
+			http.Error(w, "Loopback redirects due to invalid settings", 500)
+			return
+		}
+
+		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 - 5
mod/dynamicproxy/basicAuth.go

@@ -26,10 +26,6 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req
 		}
 	}
 
-	proxyType := "vdir-auth"
-	if pe.ProxyType == ProxyType_Subdomain {
-		proxyType = "subd-auth"
-	}
 	u, p, ok := r.BasicAuth()
 	if !ok {
 		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
@@ -48,7 +44,7 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req
 	}
 
 	if !matchingFound {
-		h.logRequest(r, false, 401, proxyType, pe.Domain)
+		h.logRequest(r, false, 401, "host", pe.Domain)
 		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
 		w.WriteHeader(401)
 		return errors.New("unauthorized")

+ 33 - 128
mod/dynamicproxy/dynamicproxy.go

@@ -22,15 +22,13 @@ import (
 
 func NewDynamicProxy(option RouterOption) (*Router, error) {
 	proxyMap := sync.Map{}
-	domainMap := sync.Map{}
 	thisRouter := Router{
-		Option:            &option,
-		ProxyEndpoints:    &proxyMap,
-		SubdomainEndpoint: &domainMap,
-		Running:           false,
-		server:            nil,
-		routingRules:      []*RoutingRule{},
-		tldMap:            map[string]int{},
+		Option:         &option,
+		ProxyEndpoints: &proxyMap,
+		Running:        false,
+		server:         nil,
+		routingRules:   []*RoutingRule{},
+		tldMap:         map[string]int{},
 	}
 
 	thisRouter.mux = &ProxyHandler{
@@ -122,7 +120,7 @@ func (router *Router) StartProxyService() error {
 						hostPath := strings.Split(r.Host, ":")
 						domainOnly = hostPath[0]
 					}
-					sep := router.getSubdomainProxyEndpointFromHostname(domainOnly)
+					sep := router.getProxyEndpointFromHostname(domainOnly)
 					if sep != nil && sep.BypassGlobalTLS {
 						//Allow routing via non-TLS handler
 						originalHostHeader := r.Host
@@ -133,7 +131,7 @@ func (router *Router) StartProxyService() error {
 							r.URL, _ = url.Parse(originalHostHeader)
 						}
 
-						sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
+						sep.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
 							ProxyDomain:  sep.Domain,
 							OriginalHost: originalHostHeader,
 							UseTLS:       sep.RequireTLS,
@@ -273,142 +271,49 @@ func (router *Router) IsProxiedSubdomain(r *http.Request) bool {
 		hostname = r.Host
 	}
 	hostname = strings.Split(hostname, ":")[0]
-	subdEndpoint := router.getSubdomainProxyEndpointFromHostname(hostname)
+	subdEndpoint := router.getProxyEndpointFromHostname(hostname)
 	return subdEndpoint != nil
 }
 
-/*
-Add an URL into a custom proxy services
-*/
-func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) error {
-	domain := options.Domain
-	if domain[len(domain)-1:] == "/" {
-		domain = domain[:len(domain)-1]
-	}
-
-	/*
-		if rootname[len(rootname)-1:] == "/" {
-			rootname = rootname[:len(rootname)-1]
-		}
-	*/
-
-	webProxyEndpoint := domain
-	if options.RequireTLS {
-		webProxyEndpoint = "https://" + webProxyEndpoint
-	} else {
-		webProxyEndpoint = "http://" + webProxyEndpoint
-	}
-	//Create a new proxy agent for this root
-	path, err := url.Parse(webProxyEndpoint)
-	if err != nil {
-		return err
-	}
-
-	proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations)
-
-	endpointObject := ProxyEndpoint{
-		ProxyType:               ProxyType_Vdir,
-		RootOrMatchingDomain:    options.RootName,
-		Domain:                  domain,
-		RequireTLS:              options.RequireTLS,
-		SkipCertValidations:     options.SkipCertValidations,
-		RequireBasicAuth:        options.RequireBasicAuth,
-		BasicAuthCredentials:    options.BasicAuthCredentials,
-		BasicAuthExceptionRules: options.BasicAuthExceptionRules,
-		Proxy:                   proxy,
-	}
-
-	router.ProxyEndpoints.Store(options.RootName, &endpointObject)
-
-	log.Println("Registered Proxy Rule: ", options.RootName+" to "+domain)
-	return nil
-}
-
 /*
 Load routing from RP
 */
-func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error) {
-	if ptype == "vdir" {
-		proxy, ok := router.ProxyEndpoints.Load(key)
+func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) {
+	var targetProxyEndpoint *ProxyEndpoint
+	router.ProxyEndpoints.Range(func(key, value interface{}) bool {
+		key, ok := key.(string)
 		if !ok {
-			return nil, errors.New("target proxy not found")
+			return true
 		}
-
-		targetProxy := proxy.(*ProxyEndpoint)
-		targetProxy.parent = router
-		return targetProxy, nil
-	} else if ptype == "subd" {
-		proxy, ok := router.SubdomainEndpoint.Load(key)
+		v, ok := value.(*ProxyEndpoint)
 		if !ok {
-			return nil, errors.New("target proxy not found")
+			return true
 		}
 
-		targetProxy := proxy.(*ProxyEndpoint)
-		targetProxy.parent = router
-		return targetProxy, nil
-	}
-
-	return nil, errors.New("unsupported ptype")
-}
+		if key == matchingDomain {
+			targetProxyEndpoint = v
+		}
+		return true
+	})
 
-/*
-Add an default router for the proxy server
-*/
-func (router *Router) SetRootProxy(options *RootOptions) error {
-	proxyLocation := options.ProxyLocation
-	if proxyLocation[len(proxyLocation)-1:] == "/" {
-		proxyLocation = proxyLocation[:len(proxyLocation)-1]
+	if targetProxyEndpoint == nil {
+		return nil, errors.New("target routing rule not found")
 	}
+	return targetProxyEndpoint, nil
+}
 
-	webProxyEndpoint := proxyLocation
-	if options.RequireTLS {
-		webProxyEndpoint = "https://" + webProxyEndpoint
-	} else {
-		webProxyEndpoint = "http://" + webProxyEndpoint
-	}
-	//Create a new proxy agent for this root
-	path, err := url.Parse(webProxyEndpoint)
+// Deep copy a proxy endpoint, excluding runtime paramters
+func CopyEndpoint(endpoint *ProxyEndpoint) *ProxyEndpoint {
+	js, _ := json.Marshal(endpoint)
+	newProxyEndpoint := ProxyEndpoint{}
+	err := json.Unmarshal(js, &newProxyEndpoint)
 	if err != nil {
-		return err
+		return nil
 	}
-
-	proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
-
-	rootEndpoint := ProxyEndpoint{
-		ProxyType:               ProxyType_Vdir,
-		RootOrMatchingDomain:    "/",
-		Domain:                  proxyLocation,
-		RequireTLS:              options.RequireTLS,
-		SkipCertValidations:     options.SkipCertValidations,
-		RequireBasicAuth:        options.RequireBasicAuth,
-		BasicAuthCredentials:    options.BasicAuthCredentials,
-		BasicAuthExceptionRules: options.BasicAuthExceptionRules,
-		Proxy:                   proxy,
-	}
-
-	router.Root = &rootEndpoint
-	return nil
-}
-
-// Helpers to export the syncmap for easier processing
-func (r *Router) GetSDProxyEndpointsAsMap() map[string]*ProxyEndpoint {
-	m := make(map[string]*ProxyEndpoint)
-	r.SubdomainEndpoint.Range(func(key, value interface{}) bool {
-		k, ok := key.(string)
-		if !ok {
-			return true
-		}
-		v, ok := value.(*ProxyEndpoint)
-		if !ok {
-			return true
-		}
-		m[k] = v
-		return true
-	})
-	return m
+	return &newProxyEndpoint
 }
 
-func (r *Router) GetVDProxyEndpointsAsMap() map[string]*ProxyEndpoint {
+func (r *Router) GetProxyEndpointsAsMap() map[string]*ProxyEndpoint {
 	m := make(map[string]*ProxyEndpoint)
 	r.ProxyEndpoints.Range(func(key, value interface{}) bool {
 		k, ok := key.(string)

+ 65 - 0
mod/dynamicproxy/endpoints.go

@@ -0,0 +1,65 @@
+package dynamicproxy
+
+import (
+	"errors"
+	"net/url"
+	"strings"
+
+	"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
+)
+
+// Prepare proxy route generate a proxy handler service object for your endpoint
+func (router *Router) PrepareProxyRoute(endpoint *ProxyEndpoint) (*ProxyEndpoint, error) {
+	//Filter the tailing slash if any
+	domain := endpoint.Domain
+	if domain[len(domain)-1:] == "/" {
+		domain = domain[:len(domain)-1]
+	}
+	endpoint.Domain = domain
+
+	//Parse the web proxy endpoint
+	webProxyEndpoint := domain
+	if !strings.HasPrefix("http://", domain) && !strings.HasPrefix("https://", domain) {
+		//TLS is not hardcoded in proxy target domain
+		if endpoint.RequireTLS {
+			webProxyEndpoint = "https://" + webProxyEndpoint
+		} else {
+			webProxyEndpoint = "http://" + webProxyEndpoint
+		}
+	}
+
+	//Create a new proxy agent for this root
+	path, err := url.Parse(webProxyEndpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	//Create the proxy routing handler
+	proxy := dpcore.NewDynamicProxyCore(path, "", endpoint.SkipCertValidations)
+	endpoint.proxy = proxy
+	endpoint.parent = router
+
+	return endpoint, nil
+}
+
+// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime
+func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error {
+	if endpoint.proxy == nil {
+		//This endpoint is not prepared
+		return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
+	}
+	// Push record into running subdomain endpoints
+	router.ProxyEndpoints.Store(endpoint.RootOrMatchingDomain, endpoint)
+	return nil
+}
+
+// Set given Proxy Route as Root. Call to PrepareProxyRoute before adding to runtime
+func (router *Router) SetProxyRouteAsRoot(endpoint *ProxyEndpoint) error {
+	if endpoint.proxy == nil {
+		//This endpoint is not prepared
+		return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
+	}
+	// Push record into running root endpoints
+	router.Root = endpoint
+	return nil
+}

+ 3 - 35
mod/dynamicproxy/proxyEndpoint.go

@@ -12,54 +12,22 @@ import "errors"
 	Most of the functions are implemented in dynamicproxy.go
 */
 
-//Get the string version of proxy type
-func (ep *ProxyEndpoint) GetProxyTypeString() string {
-	if ep.ProxyType == ProxyType_Subdomain {
-		return "subd"
-	} else if ep.ProxyType == ProxyType_Vdir {
-		return "vdir"
-	}
-
-	return "unknown"
-}
-
 //Update change in the current running proxy endpoint config
 func (ep *ProxyEndpoint) UpdateToRuntime() {
-	if ep.IsVdir() {
-		ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
-
-	} else if ep.IsSubDomain() {
-		ep.parent.SubdomainEndpoint.Store(ep.RootOrMatchingDomain, ep)
-	}
-}
-
-//Return true if the endpoint type is virtual directory
-func (ep *ProxyEndpoint) IsVdir() bool {
-	return ep.ProxyType == ProxyType_Vdir
-}
-
-//Return true if the endpoint type is subdomain
-func (ep *ProxyEndpoint) IsSubDomain() bool {
-	return ep.ProxyType == ProxyType_Subdomain
+	ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
 }
 
 //Remove this proxy endpoint from running proxy endpoint list
 func (ep *ProxyEndpoint) Remove() error {
 	//fmt.Println(ptype, key)
-	if ep.IsVdir() {
-		ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
-		return nil
-	} else if ep.IsSubDomain() {
-		ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain)
-		return nil
-	}
+	ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
 	return errors.New("invalid or unsupported type")
 
 }
 
 //ProxyEndpoint remove provide global access by key
 func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
-	targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain)
+	targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain)
 	if err != nil {
 		return err
 	}

+ 4 - 4
mod/dynamicproxy/proxyRequestHandler.go

@@ -28,9 +28,9 @@ func (router *Router) getTargetProxyEndpointFromRequestURI(requestURI string) *P
 	return targetProxyEndpoint
 }
 
-func (router *Router) getSubdomainProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
+func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
 	var targetSubdomainEndpoint *ProxyEndpoint = nil
-	ep, ok := router.SubdomainEndpoint.Load(hostname)
+	ep, ok := router.ProxyEndpoints.Load(hostname)
 	if ok {
 		targetSubdomainEndpoint = ep.(*ProxyEndpoint)
 	}
@@ -89,7 +89,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
-	err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
+	err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
 		ProxyDomain:  target.Domain,
 		OriginalHost: originalHostHeader,
 		UseTLS:       target.RequireTLS,
@@ -144,7 +144,7 @@ func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, targ
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
-	err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
+	err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
 		ProxyDomain:  target.Domain,
 		OriginalHost: originalHostHeader,
 		UseTLS:       target.RequireTLS,

+ 0 - 50
mod/dynamicproxy/subdomain.go

@@ -1,50 +0,0 @@
-package dynamicproxy
-
-import (
-	"log"
-	"net/url"
-
-	"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
-)
-
-/*
-	Add an URL intoa custom subdomain service
-
-*/
-
-func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error {
-	domain := options.Domain
-	if domain[len(domain)-1:] == "/" {
-		domain = domain[:len(domain)-1]
-	}
-
-	webProxyEndpoint := domain
-	if options.RequireTLS {
-		webProxyEndpoint = "https://" + webProxyEndpoint
-	} else {
-		webProxyEndpoint = "http://" + webProxyEndpoint
-	}
-
-	//Create a new proxy agent for this root
-	path, err := url.Parse(webProxyEndpoint)
-	if err != nil {
-		return err
-	}
-
-	proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
-
-	router.SubdomainEndpoint.Store(options.MatchingDomain, &ProxyEndpoint{
-		RootOrMatchingDomain:    options.MatchingDomain,
-		Domain:                  domain,
-		RequireTLS:              options.RequireTLS,
-		Proxy:                   proxy,
-		BypassGlobalTLS:         options.BypassGlobalTLS,
-		SkipCertValidations:     options.SkipCertValidations,
-		RequireBasicAuth:        options.RequireBasicAuth,
-		BasicAuthCredentials:    options.BasicAuthCredentials,
-		BasicAuthExceptionRules: options.BasicAuthExceptionRules,
-	})
-
-	log.Println("Adding Subdomain Rule: ", options.MatchingDomain+" to "+domain)
-	return nil
-}

+ 17 - 56
mod/dynamicproxy/typedef.go

@@ -14,8 +14,8 @@ import (
 )
 
 const (
-	ProxyType_Subdomain = 0
-	ProxyType_Vdir      = 1
+	ProxyType_Root = 0
+	ProxyType_Host = 1
 )
 
 type ProxyHandler struct {
@@ -37,15 +37,14 @@ type RouterOption struct {
 }
 
 type Router struct {
-	Option            *RouterOption
-	ProxyEndpoints    *sync.Map
-	SubdomainEndpoint *sync.Map
-	Running           bool
-	Root              *ProxyEndpoint
-	mux               http.Handler
-	server            *http.Server
-	tlsListener       net.Listener
-	routingRules      []*RoutingRule
+	Option         *RouterOption
+	ProxyEndpoints *sync.Map
+	Running        bool
+	Root           *ProxyEndpoint
+	mux            http.Handler
+	server         *http.Server
+	tlsListener    net.Listener
+	routingRules   []*RoutingRule
 
 	tlsRedirectStop chan bool      //Stop channel for tls redirection server
 	tldMap          map[string]int //Top level domain map, see tld.json
@@ -84,13 +83,16 @@ type ProxyEndpoint struct {
 
 	//Authentication
 	RequireBasicAuth        bool                      //Set to true to request basic auth before proxy
-	BasicAuthCredentials    []*BasicAuthCredentials   `json:"-"` //Basic auth credentials
+	BasicAuthCredentials    []*BasicAuthCredentials   //Basic auth credentials
 	BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
 
+	//Fallback routing logic
+	DefaultSiteOption int    //Fallback routing logic options
+	DefaultSiteValue  string //Fallback routing target, optional
+
 	//Internal Logic Elements
-	Proxy                *dpcore.ReverseProxy `json:"-"`
-	OriginalRouteOptions interface{}          //Route options from original structure, may carry additional information for edge cases
-	parent               *Router
+	parent *Router
+	proxy  *dpcore.ReverseProxy `json:"-"`
 }
 
 /*
@@ -108,47 +110,6 @@ const (
 	DefaultSite_NotFoundPage            = 3
 )
 
-type RootOptions struct {
-	ProxyLocation       string //Proxy Root target, all unset traffic will be forward to here
-	RequireTLS          bool   //Proxy root target require TLS connection (not recommended)
-	BypassGlobalTLS     bool   //Bypass global TLS setting and make root http only (not recommended)
-	SkipCertValidations bool   //Skip cert validation, suitable for self-signed certs, CURRENTLY NOT USED
-
-	//Basic Auth Related
-	RequireBasicAuth        bool //Require basic auth, CURRENTLY NOT USED
-	BasicAuthCredentials    []*BasicAuthCredentials
-	BasicAuthExceptionRules []*BasicAuthExceptionRule
-
-	//Default Site Related
-	UnknownHostOption int    //What to do when hit with unknown host. See DefaultSite_* Options
-	UnknownHostTarget string //Target if the action is redirect or proxy
-}
-
-// Virtual Directory routing options
-type VdirOptions struct {
-	RootName                string
-	Domain                  string
-	RequireTLS              bool
-	BypassGlobalTLS         bool
-	SkipCertValidations     bool
-	RequireBasicAuth        bool
-	BasicAuthCredentials    []*BasicAuthCredentials
-	BasicAuthExceptionRules []*BasicAuthExceptionRule
-}
-
-// Subdomain routing options
-type SubdOptions struct {
-	MatchingDomain          string
-	Domain                  string
-	RequireTLS              bool
-	BypassGlobalTLS         bool
-	SkipCertValidations     bool
-	RequireBasicAuth        bool
-	VirtualDirectories      []*VdirOptions
-	BasicAuthCredentials    []*BasicAuthCredentials
-	BasicAuthExceptionRules []*BasicAuthExceptionRule
-}
-
 /*
 Web Templates
 */

+ 126 - 191
reverseproxy.go

@@ -21,6 +21,9 @@ var (
 
 // Add user customizable reverse proxy
 func ReverseProxtInit() {
+	/*
+		Load Reverse Proxy Global Settings
+	*/
 	inboundPort := 80
 	if sysdb.KeyExists("settings", "inbound") {
 		sysdb.Read("settings", "inbound", &inboundPort)
@@ -63,6 +66,12 @@ func ReverseProxtInit() {
 		SystemWideLogger.Println("Force HTTPS mode disabled")
 	}
 
+	/*
+		Create a new proxy object
+		The DynamicProxy is the parent of all reverse proxy handlers,
+		use for managemening and provide functions to access proxy handlers
+	*/
+
 	dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
 		HostUUID:           nodeUUID,
 		Port:               inboundPort,
@@ -83,55 +92,28 @@ func ReverseProxtInit() {
 
 	dynamicProxyRouter = dprouter
 
-	//Load all conf from files
+	/*
+
+		Load all conf from files
+
+	*/
 	confs, _ := filepath.Glob("./conf/proxy/*.config")
-	rootConfigExists := false
 	for _, conf := range confs {
-		record, err := LoadReverseProxyConfig(conf)
+		err := LoadReverseProxyConfig(conf)
 		if err != nil {
 			SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
 			return
 		}
-
-		if record.ProxyType == "root" {
-			dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
-				ProxyLocation: record.ProxyTarget,
-				RequireTLS:    record.UseTLS,
-			})
-			rootConfigExists = true
-		} else if record.ProxyType == "subd" {
-			dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
-				MatchingDomain:          record.Rootname,
-				Domain:                  record.ProxyTarget,
-				RequireTLS:              record.UseTLS,
-				BypassGlobalTLS:         record.BypassGlobalTLS,
-				SkipCertValidations:     record.SkipTlsValidation,
-				RequireBasicAuth:        record.RequireBasicAuth,
-				BasicAuthCredentials:    record.BasicAuthCredentials,
-				BasicAuthExceptionRules: record.BasicAuthExceptionRules,
-			})
-		} else if record.ProxyType == "vdir" {
-			dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
-				RootName:                record.Rootname,
-				Domain:                  record.ProxyTarget,
-				RequireTLS:              record.UseTLS,
-				BypassGlobalTLS:         record.BypassGlobalTLS,
-				SkipCertValidations:     record.SkipTlsValidation,
-				RequireBasicAuth:        record.RequireBasicAuth,
-				BasicAuthCredentials:    record.BasicAuthCredentials,
-				BasicAuthExceptionRules: record.BasicAuthExceptionRules,
-			})
-		} else {
-			SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil)
-		}
 	}
 
-	if !rootConfigExists {
+	if dynamicProxyRouter.Root == nil {
 		//Root config not set (new deployment?), use internal static web server as root
-		dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
-			ProxyLocation: "127.0.0.1:" + staticWebServer.GetListeningPort(),
-			RequireTLS:    false,
-		})
+		defaultRootRouter, err := GetDefaultRootConfig()
+		if err != nil {
+			SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err)
+			return
+		}
+		dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
 	}
 
 	//Start Service
@@ -183,7 +165,7 @@ func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
 }
 
 func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
-	eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
+	eptype, err := utils.PostPara(r, "type") //Support root and host
 	if err != nil {
 		utils.SendErrorResponse(w, "type not defined")
 		return
@@ -251,73 +233,96 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	rootname := ""
-	if eptype == "vdir" {
-		vdir, err := utils.PostPara(r, "rootname")
+	var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
+	if eptype == "host" {
+		rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
 		if err != nil {
-			utils.SendErrorResponse(w, "vdir not defined")
+			utils.SendErrorResponse(w, "subdomain not defined")
 			return
 		}
-
-		//Vdir must start with /
-		if !strings.HasPrefix(vdir, "/") {
-			vdir = "/" + vdir
+		thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
+			//I/O
+			ProxyType:            dynamicproxy.ProxyType_Host,
+			RootOrMatchingDomain: rootOrMatchingDomain,
+			Domain:               endpoint,
+			//TLS
+			RequireTLS:          useTLS,
+			BypassGlobalTLS:     useBypassGlobalTLS,
+			SkipCertValidations: skipTlsValidation,
+			//VDir
+			VirtualDirectories: []*dynamicproxy.ProxyEndpoint{},
+			//Auth
+			RequireBasicAuth:        requireBasicAuth,
+			BasicAuthCredentials:    basicAuthCredentials,
+			BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
+			DefaultSiteOption:       0,
+			DefaultSiteValue:        "",
+		}
+
+		preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
+		if err != nil {
+			utils.SendErrorResponse(w, "unable to prepare proxy route to target endpoint: "+err.Error())
+			return
 		}
-		rootname = vdir
 
-		thisOption := dynamicproxy.VdirOptions{
-			RootName:             vdir,
-			Domain:               endpoint,
-			RequireTLS:           useTLS,
-			BypassGlobalTLS:      useBypassGlobalTLS,
-			SkipCertValidations:  skipTlsValidation,
-			RequireBasicAuth:     requireBasicAuth,
-			BasicAuthCredentials: basicAuthCredentials,
+		dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
+		proxyEndpointCreated = &thisProxyEndpoint
+	} else if eptype == "root" {
+		//Get the default site options and target
+		dsOptString, err := utils.PostPara(r, "defaultSiteOpt")
+		if err != nil {
+			utils.SendErrorResponse(w, "default site action not defined")
+			return
 		}
-		dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
 
-	} else if eptype == "subd" {
-		subdomain, err := utils.PostPara(r, "rootname")
+		var defaultSiteOption int = 1
+		opt, err := strconv.Atoi(dsOptString)
 		if err != nil {
+			utils.SendErrorResponse(w, "invalid default site option")
+			return
+		}
+
+		defaultSiteOption = opt
+
+		dsVal, err := utils.PostPara(r, "defaultSiteVal")
+		if err != nil && (defaultSiteOption == 2 || defaultSiteOption == 3) {
+			//Reverse proxy or redirect, must require value to be set
 			utils.SendErrorResponse(w, "subdomain not defined")
 			return
 		}
-		rootname = subdomain
-		thisOption := dynamicproxy.SubdOptions{
-			MatchingDomain:       subdomain,
+
+		//Write the root options to file
+		rootRoutingEndpoint := dynamicproxy.ProxyEndpoint{
+			ProxyType:            dynamicproxy.ProxyType_Root,
+			RootOrMatchingDomain: "/",
 			Domain:               endpoint,
 			RequireTLS:           useTLS,
-			BypassGlobalTLS:      useBypassGlobalTLS,
-			SkipCertValidations:  skipTlsValidation,
-			RequireBasicAuth:     requireBasicAuth,
-			BasicAuthCredentials: basicAuthCredentials,
+			BypassGlobalTLS:      false,
+			SkipCertValidations:  false,
+
+			DefaultSiteOption: defaultSiteOption,
+			DefaultSiteValue:  dsVal,
 		}
-		dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
-	} else if eptype == "root" {
-		rootname = "root"
-		thisOption := dynamicproxy.RootOptions{
-			ProxyLocation: endpoint,
-			RequireTLS:    useTLS,
+		preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
+		if err != nil {
+			utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
+			return
 		}
-		dynamicProxyRouter.SetRootProxy(&thisOption)
+
+		dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
+		proxyEndpointCreated = &rootRoutingEndpoint
 	} else {
 		//Invalid eptype
-		utils.SendErrorResponse(w, "Invalid endpoint type")
+		utils.SendErrorResponse(w, "invalid endpoint type")
 		return
 	}
 
-	//Save it
-	thisProxyConfigRecord := Record{
-		ProxyType:            eptype,
-		Rootname:             rootname,
-		ProxyTarget:          endpoint,
-		UseTLS:               useTLS,
-		BypassGlobalTLS:      useBypassGlobalTLS,
-		SkipTlsValidation:    skipTlsValidation,
-		RequireBasicAuth:     requireBasicAuth,
-		BasicAuthCredentials: basicAuthCredentials,
+	//Save the config to file
+	err = SaveReverseProxyConfig(proxyEndpointCreated)
+	if err != nil {
+		SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
+		return
 	}
-	SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
 
 	//Update utm if exists
 	if uptimeMonitor != nil {
@@ -330,17 +335,11 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
 
 /*
 ReverseProxyHandleEditEndpoint handles proxy endpoint edit
-This endpoint do not handle
-basic auth credential update. The credential
-will be loaded from old config and reused
+(host only, for root use Default Site page to edit)
+This endpoint do not handle basic auth credential update.
+The credential will be loaded from old config and reused
 */
 func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
-	eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
-	if err != nil {
-		utils.SendErrorResponse(w, "type not defined")
-		return
-	}
-
 	rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
 	if err != nil {
 		utils.SendErrorResponse(w, "Target proxy rule not defined")
@@ -381,50 +380,31 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
 	requireBasicAuth := (rba == "true")
 
 	//Load the previous basic auth credentials from current proxy rules
-	targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain)
+	targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
 	if err != nil {
 		utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
 		return
 	}
 
-	if eptype == "vdir" {
-		thisOption := dynamicproxy.VdirOptions{
-			RootName:             targetProxyEntry.RootOrMatchingDomain,
-			Domain:               endpoint,
-			RequireTLS:           useTLS,
-			BypassGlobalTLS:      false,
-			SkipCertValidations:  skipTlsValidation,
-			RequireBasicAuth:     requireBasicAuth,
-			BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
-		}
-		targetProxyEntry.Remove()
-		dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
+	//Generate a new proxyEndpoint from the new config
+	newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
+	newProxyEndpoint.Domain = endpoint
+	newProxyEndpoint.RequireTLS = useTLS
+	newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
+	newProxyEndpoint.SkipCertValidations = skipTlsValidation
+	newProxyEndpoint.RequireBasicAuth = requireBasicAuth
 
-	} else if eptype == "subd" {
-		thisOption := dynamicproxy.SubdOptions{
-			MatchingDomain:       targetProxyEntry.RootOrMatchingDomain,
-			Domain:               endpoint,
-			RequireTLS:           useTLS,
-			BypassGlobalTLS:      bypassGlobalTLS,
-			SkipCertValidations:  skipTlsValidation,
-			RequireBasicAuth:     requireBasicAuth,
-			BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
-		}
-		targetProxyEntry.Remove()
-		dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
+	//Prepare to replace the current routing rule
+	readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
 	}
+	targetProxyEntry.Remove()
+	dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
 
 	//Save it to file
-	thisProxyConfigRecord := Record{
-		ProxyType:            eptype,
-		Rootname:             targetProxyEntry.RootOrMatchingDomain,
-		ProxyTarget:          endpoint,
-		UseTLS:               useTLS,
-		SkipTlsValidation:    skipTlsValidation,
-		RequireBasicAuth:     requireBasicAuth,
-		BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
-	}
-	SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
+	SaveReverseProxyConfig(newProxyEndpoint)
 
 	//Update uptime monitor
 	UpdateUptimeMonitorTargets()
@@ -453,7 +433,7 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Remove the config from file
-	RemoveReverseProxyConfigFile(ep)
+	RemoveReverseProxyConfig(ep)
 
 	//Update utm if exists
 	if uptimeMonitor != nil {
@@ -483,14 +463,8 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		ptype, err := utils.GetPara(r, "ptype")
-		if err != nil {
-			utils.SendErrorResponse(w, "Invalid ptype given")
-			return
-		}
-
 		//Load the target proxy object from router
-		targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+		targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
 		if err != nil {
 			utils.SendErrorResponse(w, err.Error())
 			return
@@ -512,17 +486,6 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		ptype, err := utils.PostPara(r, "ptype")
-		if err != nil {
-			utils.SendErrorResponse(w, "Invalid ptype given")
-			return
-		}
-
-		if ptype != "vdir" && ptype != "subd" {
-			utils.SendErrorResponse(w, "Invalid ptype given")
-			return
-		}
-
 		creds, err := utils.PostPara(r, "creds")
 		if err != nil {
 			utils.SendErrorResponse(w, "Invalid ptype given")
@@ -530,7 +493,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
 		}
 
 		//Load the target proxy object from router
-		targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+		targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
 		if err != nil {
 			utils.SendErrorResponse(w, err.Error())
 			return
@@ -580,7 +543,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
 		targetProxy.BasicAuthCredentials = mergedCredentials
 
 		//Save it to file
-		SaveReverseProxyEndpointToFile(targetProxy)
+		SaveReverseProxyConfig(targetProxy)
 
 		//Replace runtime configuration
 		targetProxy.UpdateToRuntime()
@@ -603,14 +566,8 @@ func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	ptype, err := utils.GetPara(r, "ptype")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid ptype given")
-		return
-	}
-
 	//Load the target proxy object from router
-	targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+	targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
@@ -634,12 +591,6 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	ptype, err := utils.PostPara(r, "ptype")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid ptype given")
-		return
-	}
-
 	matchingPrefix, err := utils.PostPara(r, "prefix")
 	if err != nil {
 		utils.SendErrorResponse(w, "Invalid matching prefix given")
@@ -647,7 +598,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Load the target proxy object from router
-	targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+	targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
@@ -676,7 +627,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
 
 	//Save configs to runtime and file
 	targetProxy.UpdateToRuntime()
-	SaveReverseProxyEndpointToFile(targetProxy)
+	SaveReverseProxyConfig(targetProxy)
 
 	utils.SendOK(w)
 }
@@ -689,12 +640,6 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	ptype, err := utils.PostPara(r, "ptype")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid ptype given")
-		return
-	}
-
 	matchingPrefix, err := utils.PostPara(r, "prefix")
 	if err != nil {
 		utils.SendErrorResponse(w, "Invalid matching prefix given")
@@ -702,7 +647,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
 	}
 
 	// Load the target proxy object from router
-	targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
+	targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
@@ -727,7 +672,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
 
 	// Save configs to runtime and file
 	targetProxy.UpdateToRuntime()
-	SaveReverseProxyEndpointToFile(targetProxy)
+	SaveReverseProxyConfig(targetProxy)
 
 	utils.SendOK(w)
 }
@@ -738,16 +683,19 @@ func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
 }
 
 func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
-	eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
+	eptype, err := utils.PostPara(r, "type") //Support root and host
 	if err != nil {
 		utils.SendErrorResponse(w, "type not defined")
 		return
 	}
 
-	if eptype == "vdir" {
+	if eptype == "host" {
 		results := []*dynamicproxy.ProxyEndpoint{}
 		dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
-			results = append(results, value.(*dynamicproxy.ProxyEndpoint))
+			thisEndpoint := value.(*dynamicproxy.ProxyEndpoint)
+			//Clear the auth credentials before showing to front-end
+			thisEndpoint.BasicAuthCredentials = []*dynamicproxy.BasicAuthCredentials{}
+			results = append(results, thisEndpoint)
 			return true
 		})
 
@@ -755,19 +703,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
 			return results[i].Domain < results[j].Domain
 		})
 
-		js, _ := json.Marshal(results)
-		utils.SendJSONResponse(w, string(js))
-	} else if eptype == "subd" {
-		results := []*dynamicproxy.ProxyEndpoint{}
-		dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
-			results = append(results, value.(*dynamicproxy.ProxyEndpoint))
-			return true
-		})
-
-		sort.Slice(results, func(i, j int) bool {
-			return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
-		})
-
 		js, _ := json.Marshal(results)
 		utils.SendJSONResponse(w, string(js))
 	} else if eptype == "root" {

+ 35 - 41
web/components/rproot.html

@@ -65,13 +65,14 @@
                     <div class="ui input">
                         <input id="unsetRedirectDomain" 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://)<br>
-                    Leave empty for redirecting to upper level domain (e.g. notfound.example.com <i class="right arrow icon"></i> example.com)</small>
+                    <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 save icon" ></i> Save Options</button>
+        <button class="ui basic button" onclick="setProxyRoot()"><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>
@@ -95,7 +96,7 @@
             $("#defaultSiteProxyOptions").show();
             $("#rootReqTLS").parent().removeClass("disabled");
             $("#proxyRoot").parent().removeClass("disabled");
-            initRootInfo();
+            //initRootInfo();
         }else if (selectedDefaultSite == "redirect"){
             $("#defaultSiteRedirectOptions").show();
         }else if (selectedDefaultSite == "notfound"){
@@ -110,18 +111,30 @@
 
     //Bind events to the radio boxes
     function bindDefaultSiteRadioCheckboxEvents(){
-        $('input[type=radio][name=defaultsiteOption]').change(function() {
+        $('input[type=radio][name=defaultsiteOption]').off("change").on("change", function() {
             updateAvaibleDefaultSiteOptions();
         });
     }
     
-
-    
     function initRootInfo(callback=undefined){
         $.get("/api/proxy/list?type=root", function(data){
             if (data == null){
 
             }else{
+                var $radios = $('input:radio[name=defaultsiteOption]');
+                let proxyType = data.ProxyType;
+                //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);
+                }else if (proxyType == 2){
+                    $radios.filter('[value=redirect]').prop('checked', true);
+                }else if (proxyType == 3){
+                    $radios.filter('[value=notfound]').prop('checked', true);
+                }
+                updateAvaibleDefaultSiteOptions();
+                
                 $("#proxyRoot").val(data.Domain);
                 checkRootRequireTLS(data.Domain);
             }
@@ -163,20 +176,10 @@
         
     }
     
-    function updateRootSettingStates(){
-        $.get("/api/cert/tls", function(data){
-            if (data == true){
-                $("#disableRootTLS").parent().removeClass('disabled').attr("title", "");
-            }else{
-                $("#disableRootTLS").parent().addClass('disabled').attr("title", "TLS listener is not enabled");
-            }
-        });
-    }
 
     //Bind event to tab switch
     tabSwitchEventBind["setroot"] = function(){
-        //On switch over to this page, update root info
-        updateRootSettingStates();
+        
     }
 
     //Check if the given domain will redirect to https
@@ -200,8 +203,6 @@
                 }else if (data == "http"){
                     $("#rootReqTLS").parent().checkbox("set unchecked");
                 }
-
-                
             }
        })
     }
@@ -218,10 +219,23 @@
 
         var rootReqTls = $("#rootReqTLS")[0].checked;
 
+        //Convert the default site option to enum
+        var defaultSiteOpt = 0;
+        
+        
+
+
         //Create the endpoint by calling add
         $.ajax({
             url: "/api/proxy/add",
-            data: {"type": "root", tls: rootReqTls, ep: newpr},
+            data: {
+                "type": "root", 
+                "tls": rootReqTls, 
+                "ep": newpr,
+                "defaultSiteOpt": "",
+                "defaultSiteVal":"",
+            },
+            method: "POST",
             success: function(data){
                 if (data.error != undefined){
                     msgbox(data.error, false, 5000);
@@ -251,24 +265,4 @@
 
     }
 
-    function updateRootOptions(){
-        $.ajax({
-            type: "POST",
-            url: "/api/proxy/root/updateOptions",
-            data: {
-                unsetRedirect: $("#unsetRedirect")[0].checked,
-                unsetRedirectTarget: $("#unsetRedirectDomain").val().trim(),
-            },
-            success: function(data) {
-               if (data.error != undefined){
-                    msgbox(data.error, false);
-               }else{
-                    msgbox("Root Routing Options updated");
-               }
-            },
-            error: function(error) {
-                console.log("Error:", error);
-            }
-        });
-    }
 </script>

+ 2 - 2
web/components/rules.html

@@ -224,10 +224,10 @@
 
     //Generic functions for delete rp endpoints 
     function deleteEndpoint(ptype, epoint){
-        if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
+        if (confirm("Confirm remove proxy for :" + epoint + "?")){
             $.ajax({
                 url: "/api/proxy/del",
-                data: {ep: epoint, ptype: ptype},
+                data: {ep: epoint},
                 success: function(){
                     listVdirs();
                     listSubd();

+ 2 - 6
web/snippet/basicAuthEditor.html

@@ -99,7 +99,7 @@
                 let payloadHash = window.location.hash.substr(1);
                 try{
                     payloadHash = JSON.parse(decodeURIComponent(payloadHash));
-                    loadBasicAuthCredentials(payloadHash.ept, payloadHash.ep);
+                    loadBasicAuthCredentials(payloadHash.ep);
                     $("#epname").text(payloadHash.ep);
                     editingEndpoint = payloadHash;
                 }catch(ex){
@@ -107,13 +107,12 @@
                 }
             }
 
-            function loadBasicAuthCredentials(endpointType, uuid){
+            function loadBasicAuthCredentials(uuid){
                 $.ajax({
                     url: "/api/proxy/updateCredentials",
                     method: "GET",
                     data: {
                         ep: uuid,
-                        ptype: endpointType
                     },
                     success: function(data){
                         //Push the existing account to list
@@ -175,7 +174,6 @@
                 $.ajax({
                     url: "/api/proxy/auth/exceptions/add",
                     data:{
-                        ptype: editingEndpoint.ept,
                         ep: editingEndpoint.ep,
                         prefix: newExclusionPathMatchingPrefix
                     },
@@ -197,7 +195,6 @@
                 $.ajax({
                     url: "/api/proxy/auth/exceptions/delete",
                     data:{
-                        ptype: editingEndpoint.ept,
                         ep: editingEndpoint.ep,
                         prefix: matchingPrefix
                     },
@@ -293,7 +290,6 @@
                     method: "POST",
                     data: {
                         ep: editingEndpoint.ep, 
-                        ptype: editingEndpoint.ept,
                         creds: JSON.stringify(editingCredentials)
                     },
                     success: function(data){

+ 2 - 18
wrappers.go

@@ -119,11 +119,10 @@ func UpdateUptimeMonitorTargets() {
 
 // Generate uptime monitor targets from reverse proxy rules
 func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
-	subds := dp.GetSDProxyEndpointsAsMap()
-	vdirs := dp.GetVDProxyEndpointsAsMap()
+	hosts := dp.GetProxyEndpointsAsMap()
 
 	UptimeTargets := []*uptime.Target{}
-	for subd, target := range subds {
+	for subd, target := range hosts {
 		url := "http://" + target.Domain
 		protocol := "http"
 		if target.RequireTLS {
@@ -139,21 +138,6 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
 		})
 	}
 
-	for vdir, target := range vdirs {
-		url := "http://" + target.Domain
-		protocol := "http"
-		if target.RequireTLS {
-			url = "https://" + target.Domain
-			protocol = "https"
-		}
-		UptimeTargets = append(UptimeTargets, &uptime.Target{
-			ID:       vdir,
-			Name:     "*" + vdir,
-			URL:      url,
-			Protocol: protocol,
-		})
-	}
-
 	return UptimeTargets
 }