Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
d051606c9f
6 changed files with 322 additions and 5 deletions
  1. 4 1
      api.go
  2. 2 1
      mod/dynamicproxy/Server.go
  3. 5 2
      mod/dynamicproxy/endpoints.go
  4. 136 0
      reverseproxy.go
  5. 5 1
      web/components/httprp.html
  6. 170 0
      web/snippet/customHeaders.html

+ 4 - 1
api.go

@@ -62,7 +62,10 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
 	authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
 	authRouter.HandleFunc("/api/proxy/vdir/edit", ReverseProxyEditVdir)
-
+	//Reverse proxy user define header apis
+	authRouter.HandleFunc("/api/proxy/header/list", HandleCustomHeaderList)
+	authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd)
+	authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove)
 	//Reverse proxy auth related APIs
 	authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
 	authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)

+ 2 - 1
mod/dynamicproxy/Server.go

@@ -45,8 +45,9 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	//Inject debug headers
+	//Inject headers
 	w.Header().Set("x-proxy-by", "zoraxy/"+h.Parent.Option.HostVersion)
+	
 
 	/*
 		General Access Check

+ 5 - 2
mod/dynamicproxy/endpoints.go

@@ -26,7 +26,7 @@ import (
 // Check if a user define header exists in this endpoint, ignore case
 func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
 	for _, header := range ep.UserDefinedHeaders {
-		if strings.ToLower(header.Key) == strings.ToLower(key) {
+		if strings.EqualFold(header.Key, key) {
 			return true
 		}
 	}
@@ -38,14 +38,17 @@ func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
 func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
 	newHeaderList := []*UserDefinedHeader{}
 	for _, header := range ep.UserDefinedHeaders {
-		if strings.ToLower(header.Key) != strings.ToLower(key) {
+		if !strings.EqualFold(header.Key, key) {
 			newHeaderList = append(newHeaderList, header)
 		}
 	}
 
+	ep.UserDefinedHeaders = newHeaderList
+
 	return nil
 }
 
+// Add a user defined header to the list, duplicates will be automatically removed
 func (ep *ProxyEndpoint) AddUserDefinedHeader(key string, value string) error {
 	if ep.UserDefinedHeaderExists(key) {
 		ep.RemoveUserDefinedHeader(key)

+ 136 - 0
reverseproxy.go

@@ -866,3 +866,139 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
 
 	utils.SendOK(w)
 }
+
+/* Handle Custom Header Rules */
+//List all the custom header defined in this proxy rule
+
+func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) {
+	epType, err := utils.PostPara(r, "type")
+	if err != nil {
+		utils.SendErrorResponse(w, "endpoint type not defined")
+		return
+	}
+
+	domain, err := utils.PostPara(r, "domain")
+	if err != nil {
+		utils.SendErrorResponse(w, "domain or matching rule not defined")
+		return
+	}
+
+	var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
+	if epType == "root" {
+		targetProxyEndpoint = dynamicProxyRouter.Root
+	} else {
+		ep, err := dynamicProxyRouter.LoadProxy(domain)
+		if err != nil {
+			utils.SendErrorResponse(w, "target endpoint not exists")
+			return
+		}
+
+		targetProxyEndpoint = ep
+	}
+
+	//List all custom headers
+	customHeaderList := targetProxyEndpoint.UserDefinedHeaders
+	if customHeaderList == nil {
+		customHeaderList = []*dynamicproxy.UserDefinedHeader{}
+	}
+	js, _ := json.Marshal(customHeaderList)
+	utils.SendJSONResponse(w, string(js))
+
+}
+
+// Add a new header to the target endpoint
+func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
+	epType, err := utils.PostPara(r, "type")
+	if err != nil {
+		utils.SendErrorResponse(w, "endpoint type not defined")
+		return
+	}
+
+	domain, err := utils.PostPara(r, "domain")
+	if err != nil {
+		utils.SendErrorResponse(w, "domain or matching rule not defined")
+		return
+	}
+
+	name, err := utils.PostPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "HTTP header name not set")
+		return
+	}
+
+	value, err := utils.PostPara(r, "value")
+	if err != nil {
+		utils.SendErrorResponse(w, "HTTP header value not set")
+		return
+	}
+
+	var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
+	if epType == "root" {
+		targetProxyEndpoint = dynamicProxyRouter.Root
+	} else {
+		ep, err := dynamicProxyRouter.LoadProxy(domain)
+		if err != nil {
+			utils.SendErrorResponse(w, "target endpoint not exists")
+			return
+		}
+
+		targetProxyEndpoint = ep
+	}
+
+	//Create a new custom header object
+	targetProxyEndpoint.AddUserDefinedHeader(name, value)
+
+	//Save it (no need reload as header are not handled by dpcore)
+	err = SaveReverseProxyConfig(targetProxyEndpoint)
+	if err != nil {
+		utils.SendErrorResponse(w, "unable to save update")
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+// Remove a header from the target endpoint
+func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) {
+	epType, err := utils.PostPara(r, "type")
+	if err != nil {
+		utils.SendErrorResponse(w, "endpoint type not defined")
+		return
+	}
+
+	domain, err := utils.PostPara(r, "domain")
+	if err != nil {
+		utils.SendErrorResponse(w, "domain or matching rule not defined")
+		return
+	}
+
+	name, err := utils.PostPara(r, "name")
+	if err != nil {
+		utils.SendErrorResponse(w, "HTTP header name not set")
+		return
+	}
+
+	var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
+	if epType == "root" {
+		targetProxyEndpoint = dynamicProxyRouter.Root
+	} else {
+		ep, err := dynamicProxyRouter.LoadProxy(domain)
+		if err != nil {
+			utils.SendErrorResponse(w, "target endpoint not exists")
+			return
+		}
+
+		targetProxyEndpoint = ep
+	}
+
+	targetProxyEndpoint.RemoveUserDefinedHeader(name)
+
+	err = SaveReverseProxyConfig(targetProxyEndpoint)
+	if err != nil {
+		utils.SendErrorResponse(w, "unable to save update")
+		return
+	}
+
+	utils.SendOK(w)
+
+}

+ 5 - 1
web/components/httprp.html

@@ -256,7 +256,11 @@
     }
 
     function editCustomHeaders(uuid){
-        alert(uuid);
+        let payload = encodeURIComponent(JSON.stringify({
+            ept: "host",
+            ep: uuid
+        }));
+        showSideWrapper("snippet/customHeaders.html?t=" + Date.now() + "#" + payload);
     }
 
     function editLoadBalanceOptions(uuid){

+ 170 - 0
web/snippet/customHeaders.html

@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <!-- Notes: This should be open in its original path-->
+        <link rel="stylesheet" href="../script/semantic/semantic.min.css">
+        <script src="../script/jquery-3.6.0.min.js"></script>
+        <script src="../script/semantic/semantic.min.js"></script>
+    </head>
+    <body>
+        <br>
+        <div class="ui container">
+            <div class="ui header">
+                <div class="content">
+                    User Defined / Custom Headers
+                    <div class="sub header" id="epname"></div>
+                </div>
+            </div>
+            <div class="ui divider"></div>
+            <p>You can define custom headers to be sent 
+                together with the client request to the backend server in 
+                this reverse proxy endpoint / host.</p>
+
+            <table class="ui very basic compacted unstackable celled table">
+                <thead>
+                <tr>
+                    <th>Name</th>
+                    <th>Value</th>
+                    <th>Remove</th>
+                </tr></thead>
+                <tbody id="headerTable">
+                <tr>
+                    <td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
+                </tr>
+                </tbody>
+            </table>
+            <div class="ui divider"></div>
+            <h4>Add Custom Header</h4>
+            <p>Add custom header(s) into this proxy target</p>
+            <div class="scrolling content ui form">
+                <div class="three small fields credentialEntry">
+                    <div class="field">
+                        <input id="headerName" type="text" placeholder="X-Custom-Header" autocomplete="off">
+                    </div>
+                    <div class="field">
+                        <input id="headerValue" type="text" placeholder="value1,value2,value3" autocomplete="off">
+                    </div>
+                    <div class="field" >
+                        <button class="ui basic button" onclick="addCustomHeader();"><i class="green add icon"></i> Add Header</button>
+                    </div>
+                    <div class="ui divider"></div>
+                </div>
+            </div>
+        <script>
+            let editingEndpoint = {};
+            if (window.location.hash.length > 1){
+                let payloadHash = window.location.hash.substr(1);
+                try{
+                    payloadHash = JSON.parse(decodeURIComponent(payloadHash));
+                    $("#epname").text(payloadHash.ep);
+                    editingEndpoint = payloadHash;
+                }catch(ex){
+                    console.log("Unable to load endpoint data from hash")
+                }
+            }
+
+            //$("#debug").text(JSON.stringify(editingEndpoint));
+
+            function addCustomHeader(){
+                let name = $("#headerName").val().trim();
+                let value = $("#headerValue").val().trim();
+
+                if (name == ""){
+                    $("#headerName").parent().addClass("error");
+                    return
+                }else{
+                    $("#headerName").parent().removeClass("error");
+                }
+
+                if (value == ""){
+                    $("#headerValue").parent().addClass("error");
+                    return
+                }else{
+                    $("#headerValue").parent().removeClass("error");
+                }
+
+                $.ajax({
+                    url: "/api/proxy/header/add",
+                    data: {
+                        "type": editingEndpoint.ept,
+                        "domain": editingEndpoint.ep,
+                        "name": name,
+                        "value": value
+                    },
+                    success: function(data){
+                        if (data.error != undefined){
+                            if (parent != undefined && parent.msgbox != undefined){
+                                parent.msgbox(data.error,false);
+                            }else{
+                                alert(data.error);
+                            }
+                        }else{
+                            listCustomHeaders();
+                            if (parent != undefined && parent.msgbox != undefined){
+                                parent.msgbox("Custom header added",true);
+                            }
+
+                            //Clear the form
+                            $("#headerName").val("");
+                            $("#headerValue").val("");
+                        }
+                       
+                    }
+                });
+            }
+
+            function deleteCustomHeader(name){
+                $.ajax({
+                    url: "/api/proxy/header/remove",
+                    data: {
+                        "type": editingEndpoint.ept,
+                        "domain": editingEndpoint.ep,
+                        "name": name,
+                    },
+                    success: function(data){
+                        listCustomHeaders();
+                        if (parent != undefined && parent.msgbox != undefined){
+                            parent.msgbox("Custom header removed",true);
+                        }
+                    }
+                });
+            }
+
+            function listCustomHeaders(){
+                $("#headerTable").html(`<tr><td colspan="3"><i class="ui loading spinner icon"></i> Loading</td></tr>`);
+                $.ajax({
+                    url: "/api/proxy/header/list",
+                    data: {
+                        "type": editingEndpoint.ept,
+                        "domain": editingEndpoint.ep,
+                    },
+                    success: function(data){
+                        if (data.error != undefined){
+                            alert(data.error);
+                        }else{
+                           
+                            $("#headerTable").html("");
+                            data.forEach(header => {
+                                $("#headerTable").append(`
+                                <tr>
+                                    <td>${header.Key}</td>
+                                    <td>${header.Value}</td>
+                                    <td><button class="ui basic circular mini red icon button" onclick="deleteCustomHeader('${header.Key}');"><i class="ui trash icon"></i></button></td>
+                                </tr>
+                                `);
+                            });
+
+                            if (data.length == 0){
+                                $("#headerTable").html(`<tr>
+                                    <td colspan="3"><i class="ui green circle check icon"></i> No Additonal Header</td>
+                                </tr>`);
+                            }
+                        }
+                    },
+                    
+                });
+            }
+            listCustomHeaders();
+        </script>
+    </body>
+</html>