mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00
Custom header support
+ Added custom header + Removed unused files
This commit is contained in:
parent
216b53f224
commit
33c7c5fa00
@ -103,6 +103,9 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request)
|
|||||||
utils.SendErrorResponse(w, "ACME renew only support web server listening on port 80 (http) or 443 (https)")
|
utils.SendErrorResponse(w, "ACME renew only support web server listening on port 80 (http) or 443 (https)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Add a 3 second delay to make sure everything is settle down
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
// Pass over to the acmeHandler to deal with the communication
|
// Pass over to the acmeHandler to deal with the communication
|
||||||
acmeHandler.HandleRenewCertificate(w, r)
|
acmeHandler.HandleRenewCertificate(w, r)
|
||||||
|
|
||||||
|
@ -62,7 +62,10 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/edit", ReverseProxyEditVdir)
|
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
|
//Reverse proxy auth related APIs
|
||||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
||||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
||||||
|
@ -10,10 +10,11 @@ require (
|
|||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/grandcat/zeroconf v1.0.0
|
github.com/grandcat/zeroconf v1.0.0
|
||||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
github.com/grokify/html-strip-tags-go v0.1.0
|
||||||
github.com/likexian/whois v1.15.1
|
github.com/likexian/whois v1.15.1
|
||||||
github.com/microcosm-cc/bluemonday v1.0.25
|
github.com/microcosm-cc/bluemonday v1.0.25
|
||||||
golang.org/x/net v0.14.0
|
golang.org/x/net v0.14.0
|
||||||
golang.org/x/sys v0.11.0
|
golang.org/x/sys v0.11.0
|
||||||
|
golang.org/x/text v0.12.0
|
||||||
golang.org/x/tools v0.12.0 // indirect
|
golang.org/x/tools v0.12.0 // indirect
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Inject debug headers
|
//Inject headers
|
||||||
w.Header().Set("x-proxy-by", "zoraxy/"+h.Parent.Option.HostVersion)
|
w.Header().Set("x-proxy-by", "zoraxy/"+h.Parent.Option.HostVersion)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -228,7 +228,6 @@ func (router *Router) StopProxyService() error {
|
|||||||
// Restart the current router if it is running.
|
// Restart the current router if it is running.
|
||||||
func (router *Router) Restart() error {
|
func (router *Router) Restart() error {
|
||||||
//Stop the router if it is already running
|
//Stop the router if it is already running
|
||||||
var err error = nil
|
|
||||||
if router.Running {
|
if router.Running {
|
||||||
err := router.StopProxyService()
|
err := router.StopProxyService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -243,7 +242,7 @@ func (router *Router) Restart() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4,6 +4,9 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -16,6 +19,53 @@ import (
|
|||||||
Most of the functions are implemented in dynamicproxy.go
|
Most of the functions are implemented in dynamicproxy.go
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
User Defined Header Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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.EqualFold(header.Key, key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remvoe a user defined header from the list
|
||||||
|
func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
|
||||||
|
newHeaderList := []*UserDefinedHeader{}
|
||||||
|
for _, header := range ep.UserDefinedHeaders {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ep.UserDefinedHeaders = append(ep.UserDefinedHeaders, &UserDefinedHeader{
|
||||||
|
Key: cases.Title(language.Und, cases.NoLower).String(key), //e.g. x-proxy-by -> X-Proxy-By
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Virtual Directory Functions
|
||||||
|
*/
|
||||||
|
|
||||||
// Get virtual directory handler from given URI
|
// Get virtual directory handler from given URI
|
||||||
func (ep *ProxyEndpoint) GetVirtualDirectoryHandlerFromRequestURI(requestURI string) *VirtualDirectoryEndpoint {
|
func (ep *ProxyEndpoint) GetVirtualDirectoryHandlerFromRequestURI(requestURI string) *VirtualDirectoryEndpoint {
|
||||||
for _, vdir := range ep.VirtualDirectories {
|
for _, vdir := range ep.VirtualDirectories {
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package dynamicproxy
|
|
||||||
|
|
||||||
/*
|
|
||||||
ProxyEndpoint.go
|
|
||||||
author: tobychui
|
|
||||||
|
|
||||||
This script handle the proxy endpoint object actions
|
|
||||||
so proxyEndpoint can be handled like a proper oop object
|
|
||||||
|
|
||||||
Most of the functions are implemented in dynamicproxy.go
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Update change in the current running proxy endpoint config
|
|
||||||
func (ep *ProxyEndpoint) UpdateToRuntime() {
|
|
||||||
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove this proxy endpoint from running proxy endpoint list
|
|
||||||
func (ep *ProxyEndpoint) Remove() error {
|
|
||||||
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyEndpoint remove provide global access by key
|
|
||||||
func (router *Router) RemoveProxyEndpointByRootname(rootnameOrMatchingDomain string) error {
|
|
||||||
targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetEpt.Remove()
|
|
||||||
}
|
|
@ -88,6 +88,14 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
|||||||
func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
||||||
r.Header.Set("X-Forwarded-Host", r.Host)
|
r.Header.Set("X-Forwarded-Host", r.Host)
|
||||||
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
||||||
|
|
||||||
|
//Inject custom headers
|
||||||
|
if len(target.UserDefinedHeaders) > 0 {
|
||||||
|
for _, customHeader := range target.UserDefinedHeaders {
|
||||||
|
r.Header.Set(customHeader.Key, customHeader.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
requestURL := r.URL.String()
|
requestURL := r.URL.String()
|
||||||
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
|
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
|
||||||
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
|
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
|
||||||
@ -150,6 +158,14 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
|
|
||||||
r.Header.Set("X-Forwarded-Host", r.Host)
|
r.Header.Set("X-Forwarded-Host", r.Host)
|
||||||
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
||||||
|
|
||||||
|
//Inject custom headers
|
||||||
|
if len(target.parent.UserDefinedHeaders) > 0 {
|
||||||
|
for _, customHeader := range target.parent.UserDefinedHeaders {
|
||||||
|
r.Header.Set(customHeader.Key, customHeader.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
|
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
|
||||||
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
|
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
|
||||||
r.Header.Set("Zr-Origin-Upgrade", "websocket")
|
r.Header.Set("Zr-Origin-Upgrade", "websocket")
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package dynamicproxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
rootRoute.go
|
|
||||||
|
|
||||||
This script handle special case in routing where the root proxy
|
|
||||||
entity is involved. This also include its setting object
|
|
||||||
RootRoutingOptions
|
|
||||||
*/
|
|
||||||
|
|
||||||
var rootConfigFilepath string = "conf/root_config.json"
|
|
||||||
|
|
||||||
func loadRootRoutingOptionsFromFile() (*RootRoutingOptions, error) {
|
|
||||||
if !utils.FileExists(rootConfigFilepath) {
|
|
||||||
//Not found. Create a root option
|
|
||||||
js, _ := json.MarshalIndent(RootRoutingOptions{}, "", " ")
|
|
||||||
err := os.WriteFile(rootConfigFilepath, js, 0775)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("Unable to write root config to file: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newRootOption := RootRoutingOptions{}
|
|
||||||
rootOptionsBytes, err := os.ReadFile(rootConfigFilepath)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[Error] Unable to read root config file at " + rootConfigFilepath + ": " + err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(rootOptionsBytes, &newRootOption)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[Error] Unable to parse root config file: " + err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &newRootOption, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the new config to file. Note that this will not overwrite the runtime one
|
|
||||||
func (opt *RootRoutingOptions) SaveToFile() error {
|
|
||||||
js, _ := json.MarshalIndent(opt, "", " ")
|
|
||||||
err := os.WriteFile(rootConfigFilepath, js, 0775)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -71,6 +71,7 @@ func (router *Router) PrepareProxyRoute(endpoint *ProxyEndpoint) (*ProxyEndpoint
|
|||||||
|
|
||||||
proxy := dpcore.NewDynamicProxyCore(path, vdir.MatchingPath, vdir.SkipCertValidations)
|
proxy := dpcore.NewDynamicProxyCore(path, vdir.MatchingPath, vdir.SkipCertValidations)
|
||||||
vdir.proxy = proxy
|
vdir.proxy = proxy
|
||||||
|
vdir.parent = endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoint, nil
|
return endpoint, nil
|
||||||
|
@ -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
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -70,6 +70,12 @@ type BasicAuthExceptionRule struct {
|
|||||||
PathPrefix string
|
PathPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User defined headers to add into a proxy endpoint
|
||||||
|
type UserDefinedHeader struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
|
// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
|
||||||
// program structure than directly using ProxyEndpoint
|
// program structure than directly using ProxyEndpoint
|
||||||
type VirtualDirectoryEndpoint struct {
|
type VirtualDirectoryEndpoint struct {
|
||||||
@ -79,6 +85,7 @@ type VirtualDirectoryEndpoint struct {
|
|||||||
SkipCertValidations bool //Set to true to accept self signed certs
|
SkipCertValidations bool //Set to true to accept self signed certs
|
||||||
Disabled bool //If the rule is enabled
|
Disabled bool //If the rule is enabled
|
||||||
proxy *dpcore.ReverseProxy `json:"-"`
|
proxy *dpcore.ReverseProxy `json:"-"`
|
||||||
|
parent *ProxyEndpoint `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// A proxy endpoint record, a general interface for handling inbound routing
|
// A proxy endpoint record, a general interface for handling inbound routing
|
||||||
@ -95,6 +102,9 @@ type ProxyEndpoint struct {
|
|||||||
//Virtual Directories
|
//Virtual Directories
|
||||||
VirtualDirectories []*VirtualDirectoryEndpoint
|
VirtualDirectories []*VirtualDirectoryEndpoint
|
||||||
|
|
||||||
|
//Custom Headers
|
||||||
|
UserDefinedHeaders []*UserDefinedHeader //Custom headers to append when proxying requests from this endpoint
|
||||||
|
|
||||||
//Authentication
|
//Authentication
|
||||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||||
BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials
|
BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDuTCCAqCgAwIBAgIBADANBgkqhkiG9w0BAQ0FADB2MQswCQYDVQQGEwJoazES
|
|
||||||
MBAGA1UECAwJSG9uZyBLb25nMRQwEgYDVQQKDAtpbXVzbGFiLmNvbTEZMBcGA1UE
|
|
||||||
AwwQWm9yYXh5IFNlbGYtaG9zdDEQMA4GA1UEBwwHSU1VU0xBQjEQMA4GA1UECwwH
|
|
||||||
SU1VU0xBQjAeFw0yMzA1MjcxMDQyNDJaFw0zODA1MjgxMDQyNDJaMHYxCzAJBgNV
|
|
||||||
BAYTAmhrMRIwEAYDVQQIDAlIb25nIEtvbmcxFDASBgNVBAoMC2ltdXNsYWIuY29t
|
|
||||||
MRkwFwYDVQQDDBBab3JheHkgU2VsZi1ob3N0MRAwDgYDVQQHDAdJTVVTTEFCMRAw
|
|
||||||
DgYDVQQLDAdJTVVTTEFCMIIBIzANBgkqhkiG9w0BAQEFAAOCARAAMIIBCwKCAQIA
|
|
||||||
xav3Qq4DBooHsGW9m+r0dgjI832grX2c0Z6MJQQoE7B6wfpUI0OyfRugTXyXoiRZ
|
|
||||||
gLxuROgiCUmp8FaLbl7RsvbImMbCPo3D/RbCT1aJCNXLZ0a7yvcDYc6woQW4nUyk
|
|
||||||
ohHfT2otcu+OYS6aYRZuXGsKTAqPSwEXRMtr89wkPgZPsrCD27LFHBOmIcVABDvF
|
|
||||||
KRuiwHWSHhFfU5n1AZLyYeYoLNQ9fZPvzPpkMD+HMKi4MMwr/vLE0DwU5jSfVFq+
|
|
||||||
cd68zVihp9N/T77yah5EIH9CYm4m8Acs4bfL8DALxnaSN3KmGw6J35rOXrJvJLdh
|
|
||||||
t42PDROmQrXN8uG8wGkBiBkCAwEAAaNQME4wHQYDVR0OBBYEFLhXihE+1K6MoL0P
|
|
||||||
Nx5htfuSatpiMB8GA1UdIwQYMBaAFLhXihE+1K6MoL0PNx5htfuSatpiMAwGA1Ud
|
|
||||||
EwQFMAMBAf8wDQYJKoZIhvcNAQENBQADggECAMCn0ed1bfLefGvoQJV/q+X9p61U
|
|
||||||
HunSFJAAhp0N2Q3tq/zjIu0kJX7N0JBciEw2c0ZmqJIqR8V8Im/h/4XuuOR+53hg
|
|
||||||
opOSPo39ww7mpxyBlQm63v1nXcNQcvw4U0JqXQ4Kyv8cgX7DIuyjRWHQpc5+6joy
|
|
||||||
L5Nz5hzQbgpnPdHQEMorfnm8q6bWg/291IAV3ZA9Z6T5gn4YuyjeUdDczQtpT6nu
|
|
||||||
1iTNPqtO6R3aeTVT+OSJT9sH2MHfDAsf371HBM6MzM/5QBc/62Bgau7NUjNKeSEA
|
|
||||||
EtUBil8wBHwT7vOtqbyNk5FHEfoCpYsQtP7AtEo10izKCQpDXPftfiJefkOY
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -261,6 +261,8 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
//VDir
|
//VDir
|
||||||
VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
|
VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
|
||||||
|
//Custom headers
|
||||||
|
UserDefinedHeaders: []*dynamicproxy.UserDefinedHeader{},
|
||||||
//Auth
|
//Auth
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
BasicAuthCredentials: basicAuthCredentials,
|
||||||
@ -864,3 +866,139 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
utils.SendOK(w)
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Host</th>
|
<th>Host</th>
|
||||||
<th>Destination</th>
|
<th>Destination</th>
|
||||||
<!-- <th>Virtual Directory</th> -->
|
<th>Virtual Directory</th>
|
||||||
<th>Basic Auth</th>
|
<th>Basic Auth</th>
|
||||||
<th class="no-sort" style="min-width:100px;">Actions</th>
|
<th class="no-sort" style="min-width:100px;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -30,11 +30,11 @@
|
|||||||
$("#httpProxyList").html(``);
|
$("#httpProxyList").html(``);
|
||||||
if (data.error !== undefined){
|
if (data.error !== undefined){
|
||||||
$("#httpProxyList").append(`<tr>
|
$("#httpProxyList").append(`<tr>
|
||||||
<td data-label="" colspan="4"><i class="remove icon"></i> ${data.error}</td>
|
<td data-label="" colspan="5"><i class="remove icon"></i> ${data.error}</td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
}else if (data.length == 0){
|
}else if (data.length == 0){
|
||||||
$("#httpProxyList").append(`<tr>
|
$("#httpProxyList").append(`<tr>
|
||||||
<td data-label="" colspan="4"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
<td data-label="" colspan="5"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
}else{
|
}else{
|
||||||
data.forEach(subd => {
|
data.forEach(subd => {
|
||||||
@ -65,12 +65,13 @@
|
|||||||
vdList += `</div>`;
|
vdList += `</div>`;
|
||||||
|
|
||||||
if (subd.VirtualDirectories.length == 0){
|
if (subd.VirtualDirectories.length == 0){
|
||||||
vdList = `<i class="ui green circle check icon"></i> No Virtual Directory Rule`;
|
vdList = `<small style="opacity: 0.3; pointer-events: none; user-select: none;"><i class="check icon"></i> No Virtual Directory</small>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#httpProxyList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
$("#httpProxyList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
||||||
<td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}</td>
|
<td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}</td>
|
||||||
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
||||||
|
<td data-label="" editable="true" datatype="vdir">${vdList}</td>
|
||||||
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||||
<button class="ui circular mini basic icon button editBtn inlineEditActionBtn" onclick='editEndpoint("${(subd.RootOrMatchingDomain).hexEncode()}")'><i class="edit icon"></i></button>
|
<button class="ui circular mini basic icon button editBtn inlineEditActionBtn" onclick='editEndpoint("${(subd.RootOrMatchingDomain).hexEncode()}")'><i class="edit icon"></i></button>
|
||||||
@ -140,6 +141,12 @@
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
column.empty().append(input);
|
column.empty().append(input);
|
||||||
|
}else if (datatype == "vdir"){
|
||||||
|
//Append a quick access button for vdir page
|
||||||
|
column.append(`<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="quickEditVdir('${uuid}');">
|
||||||
|
<i class="ui yellow folder icon"></i> Edit Virtual Directories
|
||||||
|
</button>`);
|
||||||
|
|
||||||
}else if (datatype == "basicauth"){
|
}else if (datatype == "basicauth"){
|
||||||
let requireBasicAuth = payload.RequireBasicAuth;
|
let requireBasicAuth = payload.RequireBasicAuth;
|
||||||
let checkstate = "";
|
let checkstate = "";
|
||||||
@ -150,7 +157,20 @@
|
|||||||
<input type="checkbox" class="RequireBasicAuth" ${checkstate}>
|
<input type="checkbox" class="RequireBasicAuth" ${checkstate}>
|
||||||
<label>Require Basic Auth</label>
|
<label>Require Basic Auth</label>
|
||||||
</div>
|
</div>
|
||||||
<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${uuid}');">Edit Credentials</button>`);
|
<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${uuid}');"><i class="ui blue user circle icon"></i> Edit Credentials</button>
|
||||||
|
<div class="ui basic advance segment" style="padding: 0.4em !important; border-radius: 0.4em;">
|
||||||
|
<div class="ui endpointAdvanceConfig accordion" style="padding-right: 0.6em;">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advance Configs
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editCustomHeaders('${uuid}');"><i class="heading icon"></i> Custom Headers</button>
|
||||||
|
<!-- <button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editLoadBalanceOptions('${uuid}');"><i class="blue server icon"></i> Load Balance</button> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
`);
|
||||||
|
|
||||||
}else if (datatype == 'action'){
|
}else if (datatype == 'action'){
|
||||||
column.empty().append(`
|
column.empty().append(`
|
||||||
@ -173,6 +193,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".endpointAdvanceConfig").accordion();
|
||||||
$("#httpProxyList").find(".editBtn").addClass("disabled");
|
$("#httpProxyList").find(".editBtn").addClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +241,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* button events */
|
||||||
function editBasicAuthCredentials(uuid){
|
function editBasicAuthCredentials(uuid){
|
||||||
let payload = encodeURIComponent(JSON.stringify({
|
let payload = encodeURIComponent(JSON.stringify({
|
||||||
ept: "host",
|
ept: "host",
|
||||||
@ -227,6 +249,23 @@
|
|||||||
}));
|
}));
|
||||||
showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
|
showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function quickEditVdir(uuid){
|
||||||
|
openTabById("vdir");
|
||||||
|
$("#vdirBaseRoutingRule").parent().dropdown("set selected", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editCustomHeaders(uuid){
|
||||||
|
let payload = encodeURIComponent(JSON.stringify({
|
||||||
|
ept: "host",
|
||||||
|
ep: uuid
|
||||||
|
}));
|
||||||
|
showSideWrapper("snippet/customHeaders.html?t=" + Date.now() + "#" + payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editLoadBalanceOptions(uuid){
|
||||||
|
alert(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Bind on tab switch events
|
//Bind on tab switch events
|
||||||
|
@ -593,7 +593,7 @@
|
|||||||
{
|
{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
responsive: true,
|
responsive: true,
|
||||||
resizeDelay: 100,
|
resizeDelay: 300,
|
||||||
options: {
|
options: {
|
||||||
animation: false,
|
animation: false,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
<div class="standardContainer">
|
|
||||||
<div class="ui basic segment">
|
|
||||||
<h2>HTTP Proxy</h2>
|
|
||||||
<p>Proxy HTTP server with HTTP or HTTPS. Sometime subdomain contain a prefix to the main domain name, separated by a dot. <br>For example, in the domain "blog.example.com," "blog" is the subdomain.</p>
|
|
||||||
</div>
|
|
||||||
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
|
||||||
<table class="ui celled sortable unstackable compact table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Host</th>
|
|
||||||
<th>Destination</th>
|
|
||||||
<th>Virtual Directory</th>
|
|
||||||
<th>Basic Auth</th>
|
|
||||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="subdList">
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<button class="ui icon right floated basic button" onclick="listProxyEndpoints();"><i class="green refresh icon"></i> Refresh</button>
|
|
||||||
<br><br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function listProxyEndpoints(){
|
|
||||||
$.get("/api/proxy/list?type=host", function(data){
|
|
||||||
$("#subdList").html(``);
|
|
||||||
if (data.error !== undefined){
|
|
||||||
$("#subdList").append(`<tr>
|
|
||||||
<td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
|
|
||||||
</tr>`);
|
|
||||||
}else if (data.length == 0){
|
|
||||||
$("#subdList").append(`<tr>
|
|
||||||
<td data-label="" colspan="3"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
|
||||||
</tr>`);
|
|
||||||
}else{
|
|
||||||
data.forEach(subd => {
|
|
||||||
let tlsIcon = "";
|
|
||||||
let subdData = encodeURIComponent(JSON.stringify(subd));
|
|
||||||
if (subd.RequireTLS){
|
|
||||||
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
|
||||||
if (subd.SkipCertValidations){
|
|
||||||
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inboundTlsIcon = "";
|
|
||||||
if ($("#tls").checkbox("is checked")){
|
|
||||||
inboundTlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
|
||||||
if (subd.BypassGlobalTLS){
|
|
||||||
inboundTlsIcon = `<i class="grey lock icon" title="TLS Bypass Enabled"></i>`;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
inboundTlsIcon = `<i class="yellow lock open icon" title="Plain Text Mode"></i>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
|
||||||
<td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}</td>
|
|
||||||
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
|
||||||
<td data-label="" editable="true" datatype="vdir">WIP</td>
|
|
||||||
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
|
||||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
|
||||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
|
||||||
<button class="ui circular mini red basic icon button" onclick='deleteEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="trash icon"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr>`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Bind on tab switch events
|
|
||||||
tabSwitchEventBind["subd"] = function(){
|
|
||||||
listProxyEndpoints();
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -177,7 +177,7 @@
|
|||||||
$("#utmrender").append(`<div class="ui basic segment statusbar">
|
$("#utmrender").append(`<div class="ui basic segment statusbar">
|
||||||
<div class="domain">
|
<div class="domain">
|
||||||
<div style="position: absolute; top: 0; right: 0.4em;">
|
<div style="position: absolute; top: 0; right: 0.4em;">
|
||||||
<p class="onlineStatus" style="display: inline-block; font-size: 1.3em; padding-right: 0.5em; padding-left: 0.3em; ${onlineStatusCss}">${currentOnlineStatus}</p>
|
<p class="onlineStatus" style="display: inline-block; font-size: 1.2em; padding-right: 0.5em; padding-left: 0.3em; ${onlineStatusCss}">${currentOnlineStatus}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="ui header" style="margin-bottom: 0.2em;">${name}</h3>
|
<h3 class="ui header" style="margin-bottom: 0.2em;">${name}</h3>
|
||||||
|
@ -207,6 +207,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reloadVdirList(){
|
function reloadVdirList(){
|
||||||
|
$("#vdirList").find(".editBtn").removeClass("disabled");
|
||||||
if ($("#useRootProxyRouterForVdir")[0].checked){
|
if ($("#useRootProxyRouterForVdir")[0].checked){
|
||||||
loadVdirList("root");
|
loadVdirList("root");
|
||||||
return;
|
return;
|
||||||
@ -314,7 +315,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editVdir(matchingPath, ept){
|
function editVdir(matchingPath, ept){
|
||||||
let targetDOM = $(".vdirEntry[vdirid='" + matchingPath.hexEncode() + "']")
|
let targetDOM = $(".vdirEntry[vdirid='" + matchingPath.hexEncode() + "']");
|
||||||
|
$("#vdirList").find(".editBtn").addClass("disabled");
|
||||||
let payload = $(targetDOM).attr("payload").hexDecode();
|
let payload = $(targetDOM).attr("payload").hexDecode();
|
||||||
payload = JSON.parse(payload);
|
payload = JSON.parse(payload);
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 307 B |
Binary file not shown.
Before Width: | Height: | Size: 126 KiB |
Binary file not shown.
Before Width: | Height: | Size: 694 KiB |
Binary file not shown.
Before Width: | Height: | Size: 818 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB |
@ -284,11 +284,12 @@
|
|||||||
$("#mainmenu").find(".item").removeClass("active");
|
$("#mainmenu").find(".item").removeClass("active");
|
||||||
$(targetBtn).addClass("active");
|
$(targetBtn).addClass("active");
|
||||||
$(".functiontab").hide();
|
$(".functiontab").hide();
|
||||||
if (tabSwitchEventBind[tabID]){
|
|
||||||
tabSwitchEventBind[tabID]();
|
|
||||||
}
|
|
||||||
$("#" + tabID).fadeIn('fast', function(){
|
$("#" + tabID).fadeIn('fast', function(){
|
||||||
|
setTimeout(function(){
|
||||||
|
if (tabSwitchEventBind[tabID]){
|
||||||
|
tabSwitchEventBind[tabID]();
|
||||||
|
}
|
||||||
|
},100)
|
||||||
});
|
});
|
||||||
$('html,body').animate({scrollTop: 0}, 'fast');
|
$('html,body').animate({scrollTop: 0}, 'fast');
|
||||||
window.location.hash = tabID;
|
window.location.hash = tabID;
|
||||||
|
@ -107,6 +107,10 @@
|
|||||||
asc: 'sorted ascending',
|
asc: 'sorted ascending',
|
||||||
desc: 'sorted descending',
|
desc: 'sorted descending',
|
||||||
compare: function(a, b) {
|
compare: function(a, b) {
|
||||||
|
if (!isNaN(parseInt(a.trim())) && !isNaN(parseInt(b.trim())) ){
|
||||||
|
a = parseInt(a);
|
||||||
|
b = parseInt(b);
|
||||||
|
}
|
||||||
if (a > b) {
|
if (a > b) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (a < b) {
|
} else if (a < b) {
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
<!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">
|
|
||||||
<!-- Path Rules -->
|
|
||||||
<div class="ui header">
|
|
||||||
<div class="content">
|
|
||||||
Special Path Rules
|
|
||||||
<div class="sub header">Advanced customization for response on particular matching path or URL</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h4>Current list of special path rules.</h4>
|
|
||||||
<div style="width: 100%; overflow-x: auto;">
|
|
||||||
<table class="ui sortable unstackable celled table" >
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Matching Path</th>
|
|
||||||
<th>Status Code</th>
|
|
||||||
<th class="no-sort">Exact Match</th>
|
|
||||||
<th class="no-sort">Case Sensitive</th>
|
|
||||||
<th class="no-sort">Enabled</th>
|
|
||||||
<th class="no-sort">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="specialPathRules">
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<h4>Add Special Path Rule</h4>
|
|
||||||
<div class="ui form">
|
|
||||||
<div class="field">
|
|
||||||
<label>Matching URI</label>
|
|
||||||
<input type="text" name="matchingPath" placeholder="Matching URL">
|
|
||||||
<small><i class="ui circle info icon"></i> Any matching prefix of the request URL will be handled by this rule, e.g. example.com/secret</small>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" name="exactMatch" tabindex="0" class="hidden">
|
|
||||||
<label>Require Exact Match</label>
|
|
||||||
</div>
|
|
||||||
<div class="ui message">
|
|
||||||
<p>Require exactly match but not prefix match (default). Enable this if you only want to block access to a directory but not the resources inside the directory. Assume you have entered a matching URI of <b>example.com/secret/</b> and set it to return 401</p>
|
|
||||||
<i class="check square outline icon"></i> example.com/secret<b>/image.png</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> (content of image.png)<br>
|
|
||||||
<i class="square outline icon"></i> example.com/secret<b>/image.png</b> <i class="long arrow alternate right icon" style="margin-left: 1em;"></i> HTTP 401
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Response Status Code</label>
|
|
||||||
<input type="text"name="statusCode" placeholder="500">
|
|
||||||
<small><i class="ui circle info icon"></i> HTTP Status Code to be served by this rule</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br><br>
|
|
||||||
<button class="ui basic button iframeOnly" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Cancel</button>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
182
src/web/snippet/customHeaders.html
Normal file
182
src/web/snippet/customHeaders.html
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<!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">
|
||||||
|
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>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="field" >
|
||||||
|
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br><br><br><br>
|
||||||
|
|
||||||
|
<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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeThisWrapper(){
|
||||||
|
parent.hideSideWrapper(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//$("#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>
|
@ -123,7 +123,7 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
|
|||||||
hosts := dp.GetProxyEndpointsAsMap()
|
hosts := dp.GetProxyEndpointsAsMap()
|
||||||
|
|
||||||
UptimeTargets := []*uptime.Target{}
|
UptimeTargets := []*uptime.Target{}
|
||||||
for subd, target := range hosts {
|
for hostid, target := range hosts {
|
||||||
url := "http://" + target.Domain
|
url := "http://" + target.Domain
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
if target.RequireTLS {
|
if target.RequireTLS {
|
||||||
@ -131,12 +131,31 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
|
|||||||
protocol = "https"
|
protocol = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Add the root url
|
||||||
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
||||||
ID: subd,
|
ID: hostid,
|
||||||
Name: subd,
|
Name: hostid,
|
||||||
URL: url,
|
URL: url,
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Add each virtual directory into the list
|
||||||
|
for _, vdir := range target.VirtualDirectories {
|
||||||
|
url := "http://" + vdir.Domain
|
||||||
|
protocol := "http"
|
||||||
|
if target.RequireTLS {
|
||||||
|
url = "https://" + vdir.Domain
|
||||||
|
protocol = "https"
|
||||||
|
}
|
||||||
|
//Add the root url
|
||||||
|
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
||||||
|
ID: hostid + vdir.MatchingPath,
|
||||||
|
Name: hostid + vdir.MatchingPath,
|
||||||
|
URL: url,
|
||||||
|
Protocol: protocol,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return UptimeTargets
|
return UptimeTargets
|
||||||
|
Loading…
x
Reference in New Issue
Block a user