From 174efc9080a9cf21cd732dbae68e550368f06d0f Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Wed, 14 Feb 2024 22:52:56 +0800
Subject: [PATCH] Added per host vdir implementation
---
src/api.go | 9 +-
src/config.go | 5 +-
src/mod/dynamicproxy/Server.go | 42 +-
src/mod/dynamicproxy/endpoints.go | 114 +++--
src/mod/dynamicproxy/proxyRequestHandler.go | 6 +-
src/mod/dynamicproxy/router.go | 99 +++++
src/mod/dynamicproxy/typedef.go | 16 +-
src/reverseproxy.go | 4 +-
src/vdir.go | 290 +++++++++++++
src/web/components/cert.html | 2 +-
src/web/components/httprp.html | 235 ++++++++++
src/web/components/redirection.html | 21 +-
src/web/components/rproot.html | 3 +
src/web/components/rules.html | 182 ++------
src/web/components/vdir.html | 452 +++++++++++++++++---
src/web/index.html | 4 +-
src/web/main.css | 21 +-
17 files changed, 1215 insertions(+), 290 deletions(-)
create mode 100644 src/mod/dynamicproxy/router.go
create mode 100644 src/vdir.go
create mode 100644 src/web/components/httprp.html
diff --git a/src/api.go b/src/api.go
index 86dc35f..638fbdf 100644
--- a/src/api.go
+++ b/src/api.go
@@ -56,9 +56,12 @@ func initAPIs() {
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
- //Reverse proxy root related APIs
- //authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
- //authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
+ //Reverse proxy virtual directory APIs
+ authRouter.HandleFunc("/api/proxy/vdir/list", ReverseProxyListVdir)
+ authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
+ authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
+ authRouter.HandleFunc("/api/proxy/vdir/edit", ReverseProxyEditVdir)
+
//Reverse proxy auth related APIs
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
diff --git a/src/config.go b/src/config.go
index 2ab8ab1..5978769 100644
--- a/src/config.go
+++ b/src/config.go
@@ -108,8 +108,7 @@ func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error {
return err
}
- os.WriteFile(filename, js, 0775)
- return nil
+ return os.WriteFile(filename, js, 0775)
}
func RemoveReverseProxyConfig(endpoint string) error {
@@ -137,7 +136,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{},
diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go
index df69615..6e771fd 100644
--- a/src/mod/dynamicproxy/Server.go
+++ b/src/mod/dynamicproxy/Server.go
@@ -82,13 +82,31 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Host Routing
*/
sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
- if sep != nil {
+ if sep != nil && !sep.Disabled {
if sep.RequireBasicAuth {
err := h.handleBasicAuthRouting(w, r, sep)
if err != nil {
return
}
}
+
+ //Check if any virtual directory rules matches
+ proxyingPath := strings.TrimSpace(r.RequestURI)
+ targetProxyEndpoint := sep.GetVirtualDirectoryHandlerFromRequestURI(proxyingPath)
+ if targetProxyEndpoint != nil && !targetProxyEndpoint.Disabled {
+ //Virtual directory routing rule found. Route via vdir mode
+ h.vdirRequest(w, r, targetProxyEndpoint)
+ return
+ } else if !strings.HasSuffix(proxyingPath, "/") && sep.ProxyType != ProxyType_Root {
+ potentialProxtEndpoint := sep.GetVirtualDirectoryHandlerFromRequestURI(proxyingPath + "/")
+ if potentialProxtEndpoint != nil && !targetProxyEndpoint.Disabled {
+ //Missing tailing slash. Redirect to target proxy endpoint
+ http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ //Fallback to handle by the host proxy forwarder
h.hostRequest(w, r, sep)
return
}
@@ -137,7 +155,25 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
fallthrough
case DefaultSite_ReverseProxy:
//They both share the same behavior
- h.vdirRequest(w, r, h.Parent.Root)
+
+ //Check if any virtual directory rules matches
+ proxyingPath := strings.TrimSpace(r.RequestURI)
+ targetProxyEndpoint := proot.GetVirtualDirectoryHandlerFromRequestURI(proxyingPath)
+ if targetProxyEndpoint != nil && !targetProxyEndpoint.Disabled {
+ //Virtual directory routing rule found. Route via vdir mode
+ h.vdirRequest(w, r, targetProxyEndpoint)
+ return
+ } else if !strings.HasSuffix(proxyingPath, "/") && proot.ProxyType != ProxyType_Root {
+ potentialProxtEndpoint := proot.GetVirtualDirectoryHandlerFromRequestURI(proxyingPath + "/")
+ if potentialProxtEndpoint != nil && !targetProxyEndpoint.Disabled {
+ //Missing tailing slash. Redirect to target proxy endpoint
+ http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ //No vdir match. Route via root router
+ h.hostRequest(w, r, h.Parent.Root)
case DefaultSite_Redirect:
redirectTarget := strings.TrimSpace(proot.DefaultSiteValue)
if redirectTarget == "" {
@@ -148,7 +184,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.vdirRequest(w, r, h.Parent.Root)
+ h.hostRequest(w, r, h.Parent.Root)
return
}
hostname := parsedURL.Hostname()
diff --git a/src/mod/dynamicproxy/endpoints.go b/src/mod/dynamicproxy/endpoints.go
index 99c739b..0d8a760 100644
--- a/src/mod/dynamicproxy/endpoints.go
+++ b/src/mod/dynamicproxy/endpoints.go
@@ -1,65 +1,89 @@
package dynamicproxy
import (
+ "encoding/json"
"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
+/*
+ Endpoint Functions
+*/
- //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
+// Get virtual directory handler from given URI
+func (ep *ProxyEndpoint) GetVirtualDirectoryHandlerFromRequestURI(requestURI string) *VirtualDirectoryEndpoint {
+ for _, vdir := range ep.VirtualDirectories {
+ if strings.HasPrefix(requestURI, vdir.MatchingPath) {
+ return vdir
+ }
+ }
+ return nil
+}
+
+// Get virtual directory handler by matching path (exact match required)
+func (ep *ProxyEndpoint) GetVirtualDirectoryRuleByMatchingPath(matchingPath string) *VirtualDirectoryEndpoint {
+ for _, vdir := range ep.VirtualDirectories {
+ if vdir.MatchingPath == matchingPath {
+ return vdir
+ }
+ }
+ return nil
+}
+
+// Delete a vdir rule by its matching path
+func (ep *ProxyEndpoint) RemoveVirtualDirectoryRuleByMatchingPath(matchingPath string) error {
+ entryFound := false
+ newVirtualDirectoryList := []*VirtualDirectoryEndpoint{}
+ for _, vdir := range ep.VirtualDirectories {
+ if vdir.MatchingPath == matchingPath {
+ entryFound = true
} else {
- webProxyEndpoint = "http://" + webProxyEndpoint
+ newVirtualDirectoryList = append(newVirtualDirectoryList, vdir)
}
}
- //Create a new proxy agent for this root
- path, err := url.Parse(webProxyEndpoint)
+ if entryFound {
+ //Update the list of vdirs
+ ep.VirtualDirectories = newVirtualDirectoryList
+ return nil
+ }
+ return errors.New("target virtual directory routing rule not found")
+}
+
+// Delete a vdir rule by its matching path
+func (ep *ProxyEndpoint) AddVirtualDirectoryRule(vdir *VirtualDirectoryEndpoint) (*ProxyEndpoint, error) {
+ //Check for matching path duplicate
+ if ep.GetVirtualDirectoryRuleByMatchingPath(vdir.MatchingPath) != nil {
+ return nil, errors.New("rule with same matching path already exists")
+ }
+
+ //Append it to the list of virtual directory
+ ep.VirtualDirectories = append(ep.VirtualDirectories, vdir)
+
+ //Prepare to replace the current routing rule
+ parentRouter := ep.parent
+ readyRoutingRule, err := parentRouter.PrepareProxyRoute(ep)
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")
+ if ep.ProxyType == ProxyType_Root {
+ parentRouter.Root = readyRoutingRule
+ } else if ep.ProxyType == ProxyType_Host {
+ ep.Remove()
+ parentRouter.AddProxyRouteToRuntime(readyRoutingRule)
+ } else {
+ return nil, errors.New("unsupported proxy type")
}
- // Push record into running subdomain endpoints
- router.ProxyEndpoints.Store(endpoint.RootOrMatchingDomain, endpoint)
- return nil
+
+ return readyRoutingRule, 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
+// Create a deep clone object of the proxy endpoint
+// Note the returned object is not activated. Call to prepare function before pushing into runtime
+func (ep *ProxyEndpoint) Clone() *ProxyEndpoint {
+ clonedProxyEndpoint := ProxyEndpoint{}
+ js, _ := json.Marshal(ep)
+ json.Unmarshal(js, &clonedProxyEndpoint)
+ return &clonedProxyEndpoint
}
diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go
index f493cbe..3c6a119 100644
--- a/src/mod/dynamicproxy/proxyRequestHandler.go
+++ b/src/mod/dynamicproxy/proxyRequestHandler.go
@@ -129,8 +129,8 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
}
// Handle vdir type request
-func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
- rewriteURL := h.Parent.rewriteURL(target.RootOrMatchingDomain, r.RequestURI)
+func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, target *VirtualDirectoryEndpoint) {
+ rewriteURL := h.Parent.rewriteURL(target.MatchingPath, r.RequestURI)
r.URL, _ = url.Parse(rewriteURL)
r.Header.Set("X-Forwarded-Host", r.Host)
@@ -164,7 +164,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
ProxyDomain: target.Domain,
OriginalHost: originalHostHeader,
UseTLS: target.RequireTLS,
- PathPrefix: target.RootOrMatchingDomain,
+ PathPrefix: target.MatchingPath,
})
var dnsError *net.DNSError
diff --git a/src/mod/dynamicproxy/router.go b/src/mod/dynamicproxy/router.go
new file mode 100644
index 0000000..dcb1eef
--- /dev/null
+++ b/src/mod/dynamicproxy/router.go
@@ -0,0 +1,99 @@
+package dynamicproxy
+
+import (
+ "errors"
+ "net/url"
+ "strings"
+
+ "imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
+)
+
+/*
+ Dynamic Proxy Router Functions
+
+ This script handle the proxy rules router spawning
+ and preparation
+*/
+
+// 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
+
+ //Prepare proxy routing hjandler for each of the virtual directories
+ for _, vdir := range endpoint.VirtualDirectories {
+ domain := vdir.Domain
+ if domain[len(domain)-1:] == "/" {
+ domain = domain[:len(domain)-1]
+ }
+
+ //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 vdir.RequireTLS {
+ webProxyEndpoint = "https://" + webProxyEndpoint
+ } else {
+ webProxyEndpoint = "http://" + webProxyEndpoint
+ }
+ }
+
+ path, err := url.Parse(webProxyEndpoint)
+ if err != nil {
+ return nil, err
+ }
+
+ proxy := dpcore.NewDynamicProxyCore(path, vdir.MatchingPath, vdir.SkipCertValidations)
+ vdir.proxy = proxy
+ }
+
+ 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
+}
diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go
index ee6a0f8..1ae7288 100644
--- a/src/mod/dynamicproxy/typedef.go
+++ b/src/mod/dynamicproxy/typedef.go
@@ -68,10 +68,21 @@ 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
+ SkipCertValidations bool //Set to true to accept self signed certs
+ Disabled bool //If the rule is enabled
+ 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
@@ -80,7 +91,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
@@ -91,6 +102,7 @@ type ProxyEndpoint struct {
DefaultSiteOption int //Fallback routing logic options
DefaultSiteValue string //Fallback routing target, optional
+ Disabled bool //If the rule is disabled
//Internal Logic Elements
parent *Router
proxy *dpcore.ReverseProxy `json:"-"`
diff --git a/src/reverseproxy.go b/src/reverseproxy.go
index c1c56b5..2c97e3d 100644
--- a/src/reverseproxy.go
+++ b/src/reverseproxy.go
@@ -2,7 +2,6 @@ package main
import (
"encoding/json"
- "fmt"
"net/http"
"path/filepath"
"sort"
@@ -251,7 +250,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,
@@ -428,7 +427,6 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
}
//Remove the config from file
- fmt.Println(ep)
err = RemoveReverseProxyConfig(ep)
if err != nil {
utils.SendErrorResponse(w, err.Error())
diff --git a/src/vdir.go b/src/vdir.go
new file mode 100644
index 0000000..e37eefb
--- /dev/null
+++ b/src/vdir.go
@@ -0,0 +1,290 @@
+package main
+
+/*
+ Vdir.go
+
+ This script handle virtual directory functions
+ in global scopes
+
+ Author: tobychui
+*/
+
+import (
+ "encoding/json"
+ "net/http"
+ "strings"
+
+ "imuslab.com/zoraxy/mod/dynamicproxy"
+ "imuslab.com/zoraxy/mod/utils"
+)
+
+// 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))
+}
+
+// Add Virtual Directory to a host
+func ReverseProxyAddVdir(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
+ }
+
+ matchingPath, err := utils.PostPara(r, "path")
+ if err != nil {
+ utils.SendErrorResponse(w, "matching path not defined")
+ return
+ }
+
+ //Must start with /
+ if !strings.HasPrefix(matchingPath, "/") {
+ matchingPath = "/" + matchingPath
+ }
+
+ domain, err := utils.PostPara(r, "domain")
+ if err != nil {
+ utils.SendErrorResponse(w, "target domain not defined")
+ return
+ }
+
+ reqTLSStr, err := utils.PostPara(r, "reqTLS")
+ if err != nil {
+ //Assume false
+ reqTLSStr = "false"
+ }
+ reqTLS := (reqTLSStr == "true")
+
+ skipValidStr, err := utils.PostPara(r, "skipValid")
+ if err != nil {
+ //Assume false
+ skipValidStr = "false"
+ }
+
+ skipValid := (skipValidStr == "true")
+
+ //Load the target proxy endpoint from runtime
+ var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
+ if eptype == "root" {
+ //Check if root is running at reverse proxy mode
+ if dynamicProxyRouter.Root.DefaultSiteOption != dynamicproxy.DefaultSite_ReverseProxy {
+ utils.SendErrorResponse(w, "virtual directory can only be added to root router under proxy mode")
+ return
+ }
+ targetProxyEndpoint = dynamicProxyRouter.Root
+ } else if eptype == "host" {
+ endpointID, err := utils.PostPara(r, "endpoint")
+ if err != nil {
+ utils.SendErrorResponse(w, "endpoint not defined")
+ return
+ }
+
+ loadedEndpoint, err := dynamicProxyRouter.LoadProxy(endpointID)
+ if err != nil {
+ utils.SendErrorResponse(w, "selected proxy host not exists")
+ return
+ }
+
+ targetProxyEndpoint = loadedEndpoint
+ } else {
+ utils.SendErrorResponse(w, "invalid proxy type given")
+ return
+ }
+
+ // Create a virtual directory entry base on the above info
+ newVirtualDirectoryRouter := dynamicproxy.VirtualDirectoryEndpoint{
+ MatchingPath: matchingPath,
+ Domain: domain,
+ RequireTLS: reqTLS,
+ SkipCertValidations: skipValid,
+ }
+
+ //Add Virtual Directory Rule to this Proxy Endpoint
+ activatedProxyEndpoint, err := targetProxyEndpoint.AddVirtualDirectoryRule(&newVirtualDirectoryRouter)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Save it to file
+ SaveReverseProxyConfig(activatedProxyEndpoint)
+
+ // Update uptime monitor
+ UpdateUptimeMonitorTargets()
+ utils.SendOK(w)
+}
+
+func ReverseProxyDeleteVdir(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
+ }
+
+ vdir, err := utils.PostPara(r, "vdir")
+ if err != nil {
+ utils.SendErrorResponse(w, "vdir matching key not defined")
+ return
+ }
+
+ var targetEndpoint *dynamicproxy.ProxyEndpoint
+ if eptype == "root" {
+ targetEndpoint = dynamicProxyRouter.Root
+ } else if eptype == "host" {
+ //Proxy rule
+ matchingPath, err := utils.PostPara(r, "path")
+ if err != nil {
+ utils.SendErrorResponse(w, "matching path not defined")
+ return
+ }
+
+ ept, err := dynamicProxyRouter.LoadProxy(matchingPath)
+ if err != nil {
+ utils.SendErrorResponse(w, "target proxy rule not found")
+ return
+ }
+
+ targetEndpoint = ept
+ } else {
+ utils.SendErrorResponse(w, "invalid endpoint type")
+ return
+ }
+
+ //Delete the Vdir from endpoint
+ err = targetEndpoint.RemoveVirtualDirectoryRuleByMatchingPath(vdir)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ err = SaveReverseProxyConfig(targetEndpoint)
+ if err != nil {
+ SystemWideLogger.PrintAndLog("Config", "Fail to write vdir rules update to config file", err)
+ utils.SendErrorResponse(w, "unable to write changes to file")
+ return
+ }
+
+ utils.SendOK(w)
+}
+
+// Handle update of reverse proxy vdir rules
+func ReverseProxyEditVdir(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
+ }
+
+ vdir, err := utils.PostPara(r, "vdir")
+ if err != nil {
+ utils.SendErrorResponse(w, "vdir matching key not defined")
+ return
+ }
+
+ domain, err := utils.PostPara(r, "domain")
+ if err != nil {
+ utils.SendErrorResponse(w, "target domain not defined")
+ return
+ }
+
+ reqTLSStr, err := utils.PostPara(r, "reqTLS")
+ if err != nil {
+ //Assume false
+ reqTLSStr = "false"
+ }
+ reqTLS := (reqTLSStr == "true")
+
+ skipValidStr, err := utils.PostPara(r, "skipValid")
+ if err != nil {
+ //Assume false
+ skipValidStr = "false"
+ }
+
+ skipValid := (skipValidStr == "true")
+
+ var targetEndpoint *dynamicproxy.ProxyEndpoint
+ if eptype == "root" {
+ targetEndpoint = dynamicProxyRouter.Root
+
+ } else if eptype == "host" {
+ //Proxy rule
+ matchingPath, err := utils.PostPara(r, "path")
+ if err != nil {
+ utils.SendErrorResponse(w, "matching path not defined")
+ return
+ }
+
+ ept, err := dynamicProxyRouter.LoadProxy(matchingPath)
+ if err != nil {
+ utils.SendErrorResponse(w, "target proxy rule not found")
+ return
+ }
+
+ targetEndpoint = ept
+ } else {
+ utils.SendErrorResponse(w, "invalid endpoint type given")
+ return
+ }
+
+ //Check if the target vdir exists
+ if targetEndpoint.GetVirtualDirectoryRuleByMatchingPath(vdir) == nil {
+ utils.SendErrorResponse(w, "target virtual directory rule not exists")
+ return
+ }
+
+ //Overwrite the target endpoint
+ newVdirRule := dynamicproxy.VirtualDirectoryEndpoint{
+ MatchingPath: vdir,
+ Domain: domain,
+ RequireTLS: reqTLS,
+ SkipCertValidations: skipValid,
+ Disabled: false,
+ }
+
+ targetEndpoint.RemoveVirtualDirectoryRuleByMatchingPath(vdir)
+ activatedProxyEndpoint, err := targetEndpoint.AddVirtualDirectoryRule(&newVdirRule)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Save changes to file
+ SaveReverseProxyConfig(activatedProxyEndpoint)
+
+ UpdateUptimeMonitorTargets()
+
+ utils.SendOK(w)
+}
diff --git a/src/web/components/cert.html b/src/web/components/cert.html
index 1f2ab4c..c1ef9ec 100644
--- a/src/web/components/cert.html
+++ b/src/web/components/cert.html
@@ -16,7 +16,7 @@
Default Certificates
- When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one. Note that you need both of them uploaded for it to fallback properly
+
When there are no matching certificate for the requested server name, reverse proxy router will always fallback to this one. Note that you need both of them uploaded for it to fallback properly
Key Type
diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html
new file mode 100644
index 0000000..2ca49f9
--- /dev/null
+++ b/src/web/components/httprp.html
@@ -0,0 +1,235 @@
+
+
+
HTTP Proxy
+
Proxy HTTP server with HTTP or HTTPS for multiple hosts. If you are only proxying for one host / domain, use Default Site instead.
+
+
+
+
+
+
Host
+
Destination
+
+
Basic Auth
+
Actions
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/components/redirection.html b/src/web/components/redirection.html
index 66657f5..8f12009 100644
--- a/src/web/components/redirection.html
+++ b/src/web/components/redirection.html
@@ -72,25 +72,10 @@
Redirection Rules Added
-
-
-
-
-
- Advance Options
-
-
-
If you need custom header, content or status code other than basic redirects, you can use the advance path rules editor.
-
-
-
-
-
\ No newline at end of file
diff --git a/src/web/components/vdir.html b/src/web/components/vdir.html
index 7654b2d..404dbee 100644
--- a/src/web/components/vdir.html
+++ b/src/web/components/vdir.html
@@ -3,73 +3,415 @@
Virtual Directory
A virtual directory is a consolidated view of multiple directories that provides a unified entry point for users to access disparate sources.
-
-
-
-
-
Virtual Directory
-
Proxy To
-
Basic Auth
-
Actions
-
-
-
-
-
-
-
-
+
+ Select a host / routing rule to start editing Virtual Directory
+
+
+
+
Select a Target Host / Site
+
Attach Virtual Directory routing rule to root proxy router
The following are the list of Virtual Directories currently handled by the host router above
+
+
+
+
+
Virtual Directory
+
Destination
+
Actions
+
+
+
+
+
No Selected Host
+
+
+
+
+
+
+
+
+
New Virtual Directory Rule
+
+
+
-
\ No newline at end of file
diff --git a/src/web/index.html b/src/web/index.html
index 222467c..ad6d750 100644
--- a/src/web/index.html
+++ b/src/web/index.html
@@ -40,7 +40,7 @@
Default Site