mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00
Restructured proxy routing logic
- Moved virtual directory into host routing object - Generalized root and hosts routing struct - Optimized UI
This commit is contained in:
parent
36e461795a
commit
3228789375
@ -57,8 +57,8 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
||||||
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
||||||
//Reverse proxy root related APIs
|
//Reverse proxy root related APIs
|
||||||
authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
//authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
||||||
authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
|
//authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
|
||||||
//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)
|
||||||
|
173
src/config.go
173
src/config.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -35,97 +36,119 @@ type Record struct {
|
|||||||
BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
|
BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save a reverse proxy config record to file
|
/*
|
||||||
func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error {
|
Load Reverse Proxy Config from file and append it to current runtime proxy router
|
||||||
//TODO: Make this accept new def types
|
*/
|
||||||
os.MkdirAll("./conf/proxy/", 0775)
|
func LoadReverseProxyConfig(configFilepath string) error {
|
||||||
filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
|
//Load the config file from disk
|
||||||
|
endpointConfig, err := os.ReadFile(configFilepath)
|
||||||
//Generate record
|
|
||||||
thisRecord := proxyConfigRecord
|
|
||||||
|
|
||||||
//Write to file
|
|
||||||
js, _ := json.MarshalIndent(thisRecord, "", " ")
|
|
||||||
return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion)
|
|
||||||
func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error {
|
|
||||||
recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return SaveReverseProxyConfigToFile(recordToSave)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveReverseProxyConfigFile(rootname string) error {
|
//Parse it into dynamic proxy endpoint
|
||||||
filename := getFilenameFromRootName(rootname)
|
thisConfigEndpoint := dynamicproxy.ProxyEndpoint{}
|
||||||
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
err = json.Unmarshal(endpointConfig, &thisConfigEndpoint)
|
||||||
SystemWideLogger.Println("Config Removed: ", removePendingFile)
|
if err != nil {
|
||||||
if utils.FileExists(removePendingFile) {
|
return err
|
||||||
err := os.Remove(removePendingFile)
|
|
||||||
if err != nil {
|
|
||||||
SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//File already gone
|
//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 {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemWideLogger.PrintAndLog("Proxy", thisConfigEndpoint.RootOrMatchingDomain+" -> "+thisConfigEndpoint.Domain+" routing rule loaded", nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return ptype, rootname and proxyTarget, error if any
|
func filterProxyConfigFilename(filename string) string {
|
||||||
func LoadReverseProxyConfig(filename string) (*Record, error) {
|
//Filter out wildcard characters
|
||||||
thisRecord := Record{
|
filename = strings.ReplaceAll(filename, "*", "(ST)")
|
||||||
ProxyType: "",
|
filename = strings.ReplaceAll(filename, "?", "(QM)")
|
||||||
Rootname: "",
|
filename = strings.ReplaceAll(filename, "[", "(OB)")
|
||||||
ProxyTarget: "",
|
filename = strings.ReplaceAll(filename, "]", "(CB)")
|
||||||
UseTLS: false,
|
filename = strings.ReplaceAll(filename, "#", "(HT)")
|
||||||
|
return filepath.ToSlash(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error {
|
||||||
|
//Get filename for saving
|
||||||
|
filename := filepath.Join("./conf/proxy/", endpoint.RootOrMatchingDomain+".config")
|
||||||
|
if endpoint.ProxyType == dynamicproxy.ProxyType_Root {
|
||||||
|
filename = "./conf/proxy/root.config"
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = filterProxyConfigFilename(filename)
|
||||||
|
|
||||||
|
//Save config to file
|
||||||
|
js, err := json.MarshalIndent(endpoint, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
os.WriteFile(filename, js, 0775)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveReverseProxyConfig(endpoint string) error {
|
||||||
|
filename := filepath.Join("./conf/proxy/", endpoint+".config")
|
||||||
|
if endpoint == "/" {
|
||||||
|
filename = "./conf/proxy/root.config"
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = filterProxyConfigFilename(filename)
|
||||||
|
|
||||||
|
if !utils.FileExists(filename) {
|
||||||
|
return errors.New("target endpoint not exists")
|
||||||
|
}
|
||||||
|
return os.Remove(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,
|
BypassGlobalTLS: false,
|
||||||
SkipTlsValidation: false,
|
SkipCertValidations: false,
|
||||||
|
VirtualDirectories: []*dynamicproxy.ProxyEndpoint{},
|
||||||
RequireBasicAuth: false,
|
RequireBasicAuth: false,
|
||||||
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
||||||
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
||||||
}
|
DefaultSiteOption: dynamicproxy.DefaultSite_InternalStaticWebServer,
|
||||||
|
DefaultSiteValue: "",
|
||||||
configContent, err := os.ReadFile(filename)
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &thisRecord, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Unmarshal the content into config
|
return rootProxyEndpoint, nil
|
||||||
err = json.Unmarshal(configContent, &thisRecord)
|
|
||||||
if err != nil {
|
|
||||||
return &thisRecord, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Return it
|
|
||||||
return &thisRecord, 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &thisProxyConfigRecord, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFilenameFromRootName(rootname string) string {
|
|
||||||
//Generate a filename for this rootname
|
|
||||||
filename := strings.ReplaceAll(rootname, ".", "_")
|
|
||||||
filename = strings.ReplaceAll(filename, "/", "-")
|
|
||||||
filename = filename + ".config"
|
|
||||||
return filename
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,9 +49,9 @@ var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "2.6.8"
|
version = "3.0.0"
|
||||||
nodeUUID = "generic"
|
nodeUUID = "generic"
|
||||||
development = false //Set this to false to use embedded web fs
|
development = true //Set this to false to use embedded web fs
|
||||||
bootTime = time.Now().Unix()
|
bootTime = time.Now().Unix()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3,7 +3,6 @@ package dynamicproxy
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -80,38 +79,26 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Subdomain Routing
|
Host Routing
|
||||||
*/
|
*/
|
||||||
if strings.Contains(r.Host, ".") {
|
sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
|
||||||
//This might be a subdomain. See if there are any subdomain proxy router for this
|
if sep != nil {
|
||||||
sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
|
if sep.RequireBasicAuth {
|
||||||
if sep != nil {
|
err := h.handleBasicAuthRouting(w, r, sep)
|
||||||
if sep.RequireBasicAuth {
|
|
||||||
err := h.handleBasicAuthRouting(w, r, sep)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.subdomainRequest(w, r, sep)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Virtual Directory Routing
|
|
||||||
*/
|
|
||||||
//Clean up the request URI
|
|
||||||
proxyingPath := strings.TrimSpace(r.RequestURI)
|
|
||||||
targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
|
|
||||||
if targetProxyEndpoint != nil {
|
|
||||||
if targetProxyEndpoint.RequireBasicAuth {
|
|
||||||
err := h.handleBasicAuthRouting(w, r, targetProxyEndpoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.proxyRequest(w, r, targetProxyEndpoint)
|
h.hostRequest(w, r, sep)
|
||||||
} else if !strings.HasSuffix(proxyingPath, "/") {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Root Router Handling
|
||||||
|
*/
|
||||||
|
//Clean up the request URI
|
||||||
|
proxyingPath := strings.TrimSpace(r.RequestURI)
|
||||||
|
if !strings.HasSuffix(proxyingPath, "/") {
|
||||||
potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
|
potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
|
||||||
if potentialProxtEndpoint != nil {
|
if potentialProxtEndpoint != nil {
|
||||||
//Missing tailing slash. Redirect to target proxy endpoint
|
//Missing tailing slash. Redirect to target proxy endpoint
|
||||||
@ -136,52 +123,45 @@ Once entered this routing segment, the root routing options will take over
|
|||||||
for the routing logic.
|
for the routing logic.
|
||||||
*/
|
*/
|
||||||
func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
|
func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
domainOnly := r.Host
|
domainOnly := r.Host
|
||||||
if strings.Contains(r.Host, ":") {
|
if strings.Contains(r.Host, ":") {
|
||||||
hostPath := strings.Split(r.Host, ":")
|
hostPath := strings.Split(r.Host, ":")
|
||||||
domainOnly = hostPath[0]
|
domainOnly = hostPath[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
|
//Get the proxy root config
|
||||||
//Route to custom domain
|
proot := h.Parent.Root
|
||||||
if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
|
switch proot.DefaultSiteOption {
|
||||||
//Not set. Redirect to first level of domain redirectable
|
case DefaultSite_InternalStaticWebServer:
|
||||||
fld, err := h.getTopLevelRedirectableDomain(domainOnly)
|
fallthrough
|
||||||
if err != nil {
|
case DefaultSite_ReverseProxy:
|
||||||
//Redirect to proxy root
|
//They both share the same behavior
|
||||||
h.proxyRequest(w, r, h.Parent.Root)
|
h.vdirRequest(w, r, h.Parent.Root)
|
||||||
} else {
|
case DefaultSite_Redirect:
|
||||||
log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
|
redirectTarget := strings.TrimSpace(proot.DefaultSiteValue)
|
||||||
h.logRequest(r, false, 307, "root-redirect", domainOnly)
|
if redirectTarget == "" {
|
||||||
http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
|
redirectTarget = "about:blank"
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if h.isTopLevelRedirectableDomain(domainOnly) {
|
|
||||||
//This is requesting a top level private domain that should be serving 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
//Route to root
|
//Check if it is an infinite loopback redirect
|
||||||
h.proxyRequest(w, r, h.Parent.Root)
|
parsedURL, err := url.Parse(proot.DefaultSiteValue)
|
||||||
|
if err != nil {
|
||||||
|
//Error when parsing target. Send to root
|
||||||
|
h.vdirRequest(w, r, h.Parent.Root)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
case DefaultSite_NotFoundPage:
|
||||||
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
u, p, ok := r.BasicAuth()
|
||||||
if !ok {
|
if !ok {
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||||
@ -48,7 +44,7 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !matchingFound {
|
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.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
return errors.New("unauthorized")
|
return errors.New("unauthorized")
|
||||||
|
@ -22,15 +22,13 @@ import (
|
|||||||
|
|
||||||
func NewDynamicProxy(option RouterOption) (*Router, error) {
|
func NewDynamicProxy(option RouterOption) (*Router, error) {
|
||||||
proxyMap := sync.Map{}
|
proxyMap := sync.Map{}
|
||||||
domainMap := sync.Map{}
|
|
||||||
thisRouter := Router{
|
thisRouter := Router{
|
||||||
Option: &option,
|
Option: &option,
|
||||||
ProxyEndpoints: &proxyMap,
|
ProxyEndpoints: &proxyMap,
|
||||||
SubdomainEndpoint: &domainMap,
|
Running: false,
|
||||||
Running: false,
|
server: nil,
|
||||||
server: nil,
|
routingRules: []*RoutingRule{},
|
||||||
routingRules: []*RoutingRule{},
|
tldMap: map[string]int{},
|
||||||
tldMap: map[string]int{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thisRouter.mux = &ProxyHandler{
|
thisRouter.mux = &ProxyHandler{
|
||||||
@ -84,13 +82,6 @@ func (router *Router) StartProxyService() error {
|
|||||||
return errors.New("Reverse proxy router root not set")
|
return errors.New("Reverse proxy router root not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load root options from file
|
|
||||||
loadedRootOption, err := loadRootRoutingOptionsFromFile()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
router.RootRoutingOptions = loadedRootOption
|
|
||||||
|
|
||||||
minVersion := tls.VersionTLS10
|
minVersion := tls.VersionTLS10
|
||||||
if router.Option.ForceTLSLatest {
|
if router.Option.ForceTLSLatest {
|
||||||
minVersion = tls.VersionTLS12
|
minVersion = tls.VersionTLS12
|
||||||
@ -129,7 +120,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
hostPath := strings.Split(r.Host, ":")
|
hostPath := strings.Split(r.Host, ":")
|
||||||
domainOnly = hostPath[0]
|
domainOnly = hostPath[0]
|
||||||
}
|
}
|
||||||
sep := router.getSubdomainProxyEndpointFromHostname(domainOnly)
|
sep := router.getProxyEndpointFromHostname(domainOnly)
|
||||||
if sep != nil && sep.BypassGlobalTLS {
|
if sep != nil && sep.BypassGlobalTLS {
|
||||||
//Allow routing via non-TLS handler
|
//Allow routing via non-TLS handler
|
||||||
originalHostHeader := r.Host
|
originalHostHeader := r.Host
|
||||||
@ -140,7 +131,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
r.URL, _ = url.Parse(originalHostHeader)
|
r.URL, _ = url.Parse(originalHostHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
sep.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
ProxyDomain: sep.Domain,
|
ProxyDomain: sep.Domain,
|
||||||
OriginalHost: originalHostHeader,
|
OriginalHost: originalHostHeader,
|
||||||
UseTLS: sep.RequireTLS,
|
UseTLS: sep.RequireTLS,
|
||||||
@ -280,128 +271,17 @@ func (router *Router) IsProxiedSubdomain(r *http.Request) bool {
|
|||||||
hostname = r.Host
|
hostname = r.Host
|
||||||
}
|
}
|
||||||
hostname = strings.Split(hostname, ":")[0]
|
hostname = strings.Split(hostname, ":")[0]
|
||||||
subdEndpoint := router.getSubdomainProxyEndpointFromHostname(hostname)
|
subdEndpoint := router.getProxyEndpointFromHostname(hostname)
|
||||||
return subdEndpoint != nil
|
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
|
Load routing from RP
|
||||||
*/
|
*/
|
||||||
func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error) {
|
func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) {
|
||||||
if ptype == "vdir" {
|
var targetProxyEndpoint *ProxyEndpoint
|
||||||
proxy, ok := router.ProxyEndpoints.Load(key)
|
router.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||||
if !ok {
|
key, ok := key.(string)
|
||||||
return nil, errors.New("target proxy not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetProxy := proxy.(*ProxyEndpoint)
|
|
||||||
targetProxy.parent = router
|
|
||||||
return targetProxy, nil
|
|
||||||
} else if ptype == "subd" {
|
|
||||||
proxy, ok := router.SubdomainEndpoint.Load(key)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("target proxy not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetProxy := proxy.(*ProxyEndpoint)
|
|
||||||
targetProxy.parent = router
|
|
||||||
return targetProxy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("unsupported ptype")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -409,13 +289,32 @@ func (r *Router) GetSDProxyEndpointsAsMap() map[string]*ProxyEndpoint {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
m[k] = v
|
|
||||||
|
if key == matchingDomain {
|
||||||
|
targetProxyEndpoint = v
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return m
|
|
||||||
|
if targetProxyEndpoint == nil {
|
||||||
|
return nil, errors.New("target routing rule not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetProxyEndpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) GetVDProxyEndpointsAsMap() map[string]*ProxyEndpoint {
|
// 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 nil
|
||||||
|
}
|
||||||
|
return &newProxyEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) GetProxyEndpointsAsMap() map[string]*ProxyEndpoint {
|
||||||
m := make(map[string]*ProxyEndpoint)
|
m := make(map[string]*ProxyEndpoint)
|
||||||
r.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
r.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||||
k, ok := key.(string)
|
k, ok := key.(string)
|
||||||
|
65
src/mod/dynamicproxy/endpoints.go
Normal file
65
src/mod/dynamicproxy/endpoints.go
Normal file
@ -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
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package dynamicproxy
|
package dynamicproxy
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ProxyEndpoint.go
|
ProxyEndpoint.go
|
||||||
author: tobychui
|
author: tobychui
|
||||||
@ -12,54 +10,20 @@ import "errors"
|
|||||||
Most of the functions are implemented in dynamicproxy.go
|
Most of the functions are implemented in dynamicproxy.go
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//Get the string version of proxy type
|
// Update change in the current running proxy endpoint config
|
||||||
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() {
|
func (ep *ProxyEndpoint) UpdateToRuntime() {
|
||||||
if ep.IsVdir() {
|
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
||||||
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
|
// Remove this proxy endpoint from running proxy endpoint list
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove this proxy endpoint from running proxy endpoint list
|
|
||||||
func (ep *ProxyEndpoint) Remove() error {
|
func (ep *ProxyEndpoint) Remove() error {
|
||||||
//fmt.Println(ptype, key)
|
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
||||||
if ep.IsVdir() {
|
return nil
|
||||||
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
|
||||||
return nil
|
|
||||||
} else if ep.IsSubDomain() {
|
|
||||||
ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("invalid or unsupported type")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//ProxyEndpoint remove provide global access by key
|
// ProxyEndpoint remove provide global access by key
|
||||||
func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
|
func (router *Router) RemoveProxyEndpointByRootname(rootnameOrMatchingDomain string) error {
|
||||||
targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain)
|
targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
||||||
@ -28,13 +29,28 @@ func (router *Router) getTargetProxyEndpointFromRequestURI(requestURI string) *P
|
|||||||
return targetProxyEndpoint
|
return targetProxyEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (router *Router) getSubdomainProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
|
func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
|
||||||
var targetSubdomainEndpoint *ProxyEndpoint = nil
|
var targetSubdomainEndpoint *ProxyEndpoint = nil
|
||||||
ep, ok := router.SubdomainEndpoint.Load(hostname)
|
ep, ok := router.ProxyEndpoints.Load(hostname)
|
||||||
if ok {
|
if ok {
|
||||||
targetSubdomainEndpoint = ep.(*ProxyEndpoint)
|
targetSubdomainEndpoint = ep.(*ProxyEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//No hit. Try with wildcard
|
||||||
|
router.ProxyEndpoints.Range(func(k, v interface{}) bool {
|
||||||
|
ep := v.(*ProxyEndpoint)
|
||||||
|
match, err := filepath.Match(ep.RootOrMatchingDomain, hostname)
|
||||||
|
if err != nil {
|
||||||
|
//Continue
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
targetSubdomainEndpoint = ep
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
return targetSubdomainEndpoint
|
return targetSubdomainEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +70,8 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
|||||||
return rewrittenURL
|
return rewrittenURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle subdomain request
|
// Handle host request
|
||||||
func (h *ProxyHandler) subdomainRequest(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)
|
||||||
requestURL := r.URL.String()
|
requestURL := r.URL.String()
|
||||||
@ -89,7 +105,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
|
|||||||
r.URL, _ = url.Parse(originalHostHeader)
|
r.URL, _ = url.Parse(originalHostHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
ProxyDomain: target.Domain,
|
ProxyDomain: target.Domain,
|
||||||
OriginalHost: originalHostHeader,
|
OriginalHost: originalHostHeader,
|
||||||
UseTLS: target.RequireTLS,
|
UseTLS: target.RequireTLS,
|
||||||
@ -113,7 +129,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle vdir type request
|
// Handle vdir type request
|
||||||
func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
||||||
rewriteURL := h.Parent.rewriteURL(target.RootOrMatchingDomain, r.RequestURI)
|
rewriteURL := h.Parent.rewriteURL(target.RootOrMatchingDomain, r.RequestURI)
|
||||||
r.URL, _ = url.Parse(rewriteURL)
|
r.URL, _ = url.Parse(rewriteURL)
|
||||||
|
|
||||||
@ -144,7 +160,7 @@ func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, targ
|
|||||||
r.URL, _ = url.Parse(originalHostHeader)
|
r.URL, _ = url.Parse(originalHostHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
ProxyDomain: target.Domain,
|
ProxyDomain: target.Domain,
|
||||||
OriginalHost: originalHostHeader,
|
OriginalHost: originalHostHeader,
|
||||||
UseTLS: target.RequireTLS,
|
UseTLS: target.RequireTLS,
|
||||||
|
@ -14,8 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProxyType_Subdomain = 0
|
ProxyType_Root = 0
|
||||||
ProxyType_Vdir = 1
|
ProxyType_Host = 1
|
||||||
|
ProxyType_Vdir = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyHandler struct {
|
type ProxyHandler struct {
|
||||||
@ -37,16 +38,14 @@ type RouterOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
Option *RouterOption
|
Option *RouterOption
|
||||||
ProxyEndpoints *sync.Map
|
ProxyEndpoints *sync.Map
|
||||||
SubdomainEndpoint *sync.Map
|
Running bool
|
||||||
Running bool
|
Root *ProxyEndpoint
|
||||||
Root *ProxyEndpoint
|
mux http.Handler
|
||||||
RootRoutingOptions *RootRoutingOptions
|
server *http.Server
|
||||||
mux http.Handler
|
tlsListener net.Listener
|
||||||
server *http.Server
|
routingRules []*RoutingRule
|
||||||
tlsListener net.Listener
|
|
||||||
routingRules []*RoutingRule
|
|
||||||
|
|
||||||
tlsRedirectStop chan bool //Stop channel for tls redirection server
|
tlsRedirectStop chan bool //Stop channel for tls redirection server
|
||||||
tldMap map[string]int //Top level domain map, see tld.json
|
tldMap map[string]int //Top level domain map, see tld.json
|
||||||
@ -69,63 +68,48 @@ type BasicAuthExceptionRule struct {
|
|||||||
PathPrefix string
|
PathPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// A proxy endpoint record
|
// A proxy endpoint record, a general interface for handling inbound routing
|
||||||
type ProxyEndpoint struct {
|
type ProxyEndpoint struct {
|
||||||
ProxyType int //The type of this proxy, see const def
|
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 //Root for vdir or Matching domain for subd, also act as key
|
||||||
Domain string //Domain or IP to proxy to
|
Domain string //Domain or IP to proxy to
|
||||||
RequireTLS bool //Target domain require TLS
|
|
||||||
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
|
||||||
SkipCertValidations bool //Set to true to accept self signed certs
|
|
||||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
|
||||||
BasicAuthCredentials []*BasicAuthCredentials `json:"-"` //Basic auth credentials
|
|
||||||
BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
|
|
||||||
Proxy *dpcore.ReverseProxy `json:"-"`
|
|
||||||
|
|
||||||
|
//TLS/SSL Related
|
||||||
|
RequireTLS bool //Target domain require TLS
|
||||||
|
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||||
|
SkipCertValidations bool //Set to true to accept self signed certs
|
||||||
|
|
||||||
|
//Virtual Directories
|
||||||
|
VirtualDirectories []*ProxyEndpoint
|
||||||
|
|
||||||
|
//Authentication
|
||||||
|
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||||
|
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
|
||||||
parent *Router
|
parent *Router
|
||||||
|
proxy *dpcore.ReverseProxy `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Routing type specific interface
|
||||||
|
These are options that only avaible for a specific interface
|
||||||
|
when running, these are converted into "ProxyEndpoint" objects
|
||||||
|
for more generic routing logic
|
||||||
|
*/
|
||||||
|
|
||||||
// Root options are those that are required for reverse proxy handler to work
|
// Root options are those that are required for reverse proxy handler to work
|
||||||
type RootOptions struct {
|
const (
|
||||||
ProxyLocation string //Proxy Root target, all unset traffic will be forward to here
|
DefaultSite_InternalStaticWebServer = 0
|
||||||
RequireTLS bool //Proxy root target require TLS connection (not recommended)
|
DefaultSite_ReverseProxy = 1
|
||||||
BypassGlobalTLS bool //Bypass global TLS setting and make root http only (not recommended)
|
DefaultSite_Redirect = 2
|
||||||
SkipCertValidations bool //Skip cert validation, suitable for self-signed certs, CURRENTLY NOT USED
|
DefaultSite_NotFoundPage = 3
|
||||||
|
)
|
||||||
//Basic Auth Related
|
|
||||||
RequireBasicAuth bool //Require basic auth, CURRENTLY NOT USED
|
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
|
||||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional options are here for letting router knows how to route exception cases for root
|
|
||||||
type RootRoutingOptions struct {
|
|
||||||
//Root only configs
|
|
||||||
EnableRedirectForUnsetRules bool //Force unset rules to redirect to custom domain
|
|
||||||
UnsetRuleRedirectTarget string //Custom domain to redirect to for unset rules
|
|
||||||
}
|
|
||||||
|
|
||||||
type VdirOptions struct {
|
|
||||||
RootName string
|
|
||||||
Domain string
|
|
||||||
RequireTLS bool
|
|
||||||
BypassGlobalTLS bool
|
|
||||||
SkipCertValidations bool
|
|
||||||
RequireBasicAuth bool
|
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
|
||||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubdOptions struct {
|
|
||||||
MatchingDomain string
|
|
||||||
Domain string
|
|
||||||
RequireTLS bool
|
|
||||||
BypassGlobalTLS bool
|
|
||||||
SkipCertValidations bool
|
|
||||||
RequireBasicAuth bool
|
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
|
||||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Web Templates
|
Web Templates
|
||||||
|
@ -89,7 +89,7 @@ func (ws *WebServer) RestorePreviousState() {
|
|||||||
ws.option.EnableDirectoryListing = enableDirList
|
ws.option.EnableDirectoryListing = enableDirList
|
||||||
|
|
||||||
//Check the running state
|
//Check the running state
|
||||||
webservRunning := false
|
webservRunning := true
|
||||||
ws.option.Sysdb.Read("webserv", "enabled", &webservRunning)
|
ws.option.Sysdb.Read("webserv", "enabled", &webservRunning)
|
||||||
if webservRunning {
|
if webservRunning {
|
||||||
ws.Start()
|
ws.Start()
|
||||||
@ -124,6 +124,11 @@ func (ws *WebServer) ChangePort(port string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current using port in options
|
||||||
|
func (ws *WebServer) GetListeningPort() string {
|
||||||
|
return ws.option.Port
|
||||||
|
}
|
||||||
|
|
||||||
// Start starts the web server.
|
// Start starts the web server.
|
||||||
func (ws *WebServer) Start() error {
|
func (ws *WebServer) Start() error {
|
||||||
ws.mu.Lock()
|
ws.mu.Lock()
|
||||||
|
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@ -21,6 +22,9 @@ var (
|
|||||||
|
|
||||||
// Add user customizable reverse proxy
|
// Add user customizable reverse proxy
|
||||||
func ReverseProxtInit() {
|
func ReverseProxtInit() {
|
||||||
|
/*
|
||||||
|
Load Reverse Proxy Global Settings
|
||||||
|
*/
|
||||||
inboundPort := 80
|
inboundPort := 80
|
||||||
if sysdb.KeyExists("settings", "inbound") {
|
if sysdb.KeyExists("settings", "inbound") {
|
||||||
sysdb.Read("settings", "inbound", &inboundPort)
|
sysdb.Read("settings", "inbound", &inboundPort)
|
||||||
@ -63,6 +67,12 @@ func ReverseProxtInit() {
|
|||||||
SystemWideLogger.Println("Force HTTPS mode disabled")
|
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{
|
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
||||||
HostUUID: nodeUUID,
|
HostUUID: nodeUUID,
|
||||||
Port: inboundPort,
|
Port: inboundPort,
|
||||||
@ -83,45 +93,28 @@ func ReverseProxtInit() {
|
|||||||
|
|
||||||
dynamicProxyRouter = dprouter
|
dynamicProxyRouter = dprouter
|
||||||
|
|
||||||
//Load all conf from files
|
/*
|
||||||
|
|
||||||
|
Load all conf from files
|
||||||
|
|
||||||
|
*/
|
||||||
confs, _ := filepath.Glob("./conf/proxy/*.config")
|
confs, _ := filepath.Glob("./conf/proxy/*.config")
|
||||||
for _, conf := range confs {
|
for _, conf := range confs {
|
||||||
record, err := LoadReverseProxyConfig(conf)
|
err := LoadReverseProxyConfig(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if record.ProxyType == "root" {
|
if dynamicProxyRouter.Root == nil {
|
||||||
dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
|
//Root config not set (new deployment?), use internal static web server as root
|
||||||
ProxyLocation: record.ProxyTarget,
|
defaultRootRouter, err := GetDefaultRootConfig()
|
||||||
RequireTLS: record.UseTLS,
|
if err != nil {
|
||||||
})
|
SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err)
|
||||||
} else if record.ProxyType == "subd" {
|
return
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Start Service
|
//Start Service
|
||||||
@ -173,7 +166,7 @@ func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReverseProxyHandleAddEndpoint(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 {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "type not defined")
|
utils.SendErrorResponse(w, "type not defined")
|
||||||
return
|
return
|
||||||
@ -241,73 +234,96 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootname := ""
|
var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
|
||||||
if eptype == "vdir" {
|
if eptype == "host" {
|
||||||
vdir, err := utils.PostPara(r, "rootname")
|
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "vdir not defined")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Vdir must start with /
|
|
||||||
if !strings.HasPrefix(vdir, "/") {
|
|
||||||
vdir = "/" + vdir
|
|
||||||
}
|
|
||||||
rootname = vdir
|
|
||||||
|
|
||||||
thisOption := dynamicproxy.VdirOptions{
|
|
||||||
RootName: vdir,
|
|
||||||
Domain: endpoint,
|
|
||||||
RequireTLS: useTLS,
|
|
||||||
BypassGlobalTLS: useBypassGlobalTLS,
|
|
||||||
SkipCertValidations: skipTlsValidation,
|
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
|
||||||
}
|
|
||||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
|
||||||
|
|
||||||
} else if eptype == "subd" {
|
|
||||||
subdomain, err := utils.PostPara(r, "rootname")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "subdomain not defined")
|
utils.SendErrorResponse(w, "subdomain not defined")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rootname = subdomain
|
thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
|
||||||
thisOption := dynamicproxy.SubdOptions{
|
//I/O
|
||||||
MatchingDomain: subdomain,
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == 1 || defaultSiteOption == 2) {
|
||||||
|
//Reverse proxy or redirect, must require value to be set
|
||||||
|
utils.SendErrorResponse(w, "target not defined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write the root options to file
|
||||||
|
rootRoutingEndpoint := dynamicproxy.ProxyEndpoint{
|
||||||
|
ProxyType: dynamicproxy.ProxyType_Root,
|
||||||
|
RootOrMatchingDomain: "/",
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
BypassGlobalTLS: useBypassGlobalTLS,
|
BypassGlobalTLS: false,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: false,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
DefaultSiteOption: defaultSiteOption,
|
||||||
|
DefaultSiteValue: dsVal,
|
||||||
}
|
}
|
||||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
|
||||||
} else if eptype == "root" {
|
if err != nil {
|
||||||
rootname = "root"
|
utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
|
||||||
thisOption := dynamicproxy.RootOptions{
|
return
|
||||||
ProxyLocation: endpoint,
|
|
||||||
RequireTLS: useTLS,
|
|
||||||
}
|
}
|
||||||
dynamicProxyRouter.SetRootProxy(&thisOption)
|
|
||||||
|
dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
|
||||||
|
proxyEndpointCreated = &rootRoutingEndpoint
|
||||||
} else {
|
} else {
|
||||||
//Invalid eptype
|
//Invalid eptype
|
||||||
utils.SendErrorResponse(w, "Invalid endpoint type")
|
utils.SendErrorResponse(w, "invalid endpoint type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save it
|
//Save the config to file
|
||||||
thisProxyConfigRecord := Record{
|
err = SaveReverseProxyConfig(proxyEndpointCreated)
|
||||||
ProxyType: eptype,
|
if err != nil {
|
||||||
Rootname: rootname,
|
SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
|
||||||
ProxyTarget: endpoint,
|
return
|
||||||
UseTLS: useTLS,
|
|
||||||
BypassGlobalTLS: useBypassGlobalTLS,
|
|
||||||
SkipTlsValidation: skipTlsValidation,
|
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
|
||||||
}
|
}
|
||||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
|
||||||
|
|
||||||
//Update utm if exists
|
//Update utm if exists
|
||||||
if uptimeMonitor != nil {
|
if uptimeMonitor != nil {
|
||||||
@ -320,17 +336,11 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
ReverseProxyHandleEditEndpoint handles proxy endpoint edit
|
ReverseProxyHandleEditEndpoint handles proxy endpoint edit
|
||||||
This endpoint do not handle
|
(host only, for root use Default Site page to edit)
|
||||||
basic auth credential update. The credential
|
This endpoint do not handle basic auth credential update.
|
||||||
will be loaded from old config and reused
|
The credential will be loaded from old config and reused
|
||||||
*/
|
*/
|
||||||
func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
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")
|
rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Target proxy rule not defined")
|
utils.SendErrorResponse(w, "Target proxy rule not defined")
|
||||||
@ -371,50 +381,31 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
requireBasicAuth := (rba == "true")
|
requireBasicAuth := (rba == "true")
|
||||||
|
|
||||||
//Load the previous basic auth credentials from current proxy rules
|
//Load the previous basic auth credentials from current proxy rules
|
||||||
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain)
|
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
|
utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if eptype == "vdir" {
|
//Generate a new proxyEndpoint from the new config
|
||||||
thisOption := dynamicproxy.VdirOptions{
|
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
||||||
RootName: targetProxyEntry.RootOrMatchingDomain,
|
newProxyEndpoint.Domain = endpoint
|
||||||
Domain: endpoint,
|
newProxyEndpoint.RequireTLS = useTLS
|
||||||
RequireTLS: useTLS,
|
newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
|
||||||
BypassGlobalTLS: false,
|
newProxyEndpoint.SkipCertValidations = skipTlsValidation
|
||||||
SkipCertValidations: skipTlsValidation,
|
newProxyEndpoint.RequireBasicAuth = requireBasicAuth
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
|
||||||
}
|
|
||||||
targetProxyEntry.Remove()
|
|
||||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
|
||||||
|
|
||||||
} else if eptype == "subd" {
|
//Prepare to replace the current routing rule
|
||||||
thisOption := dynamicproxy.SubdOptions{
|
readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
|
||||||
MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
|
if err != nil {
|
||||||
Domain: endpoint,
|
utils.SendErrorResponse(w, err.Error())
|
||||||
RequireTLS: useTLS,
|
return
|
||||||
BypassGlobalTLS: bypassGlobalTLS,
|
|
||||||
SkipCertValidations: skipTlsValidation,
|
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
|
||||||
}
|
|
||||||
targetProxyEntry.Remove()
|
|
||||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
|
||||||
}
|
}
|
||||||
|
targetProxyEntry.Remove()
|
||||||
|
dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
|
||||||
|
|
||||||
//Save it to file
|
//Save it to file
|
||||||
thisProxyConfigRecord := Record{
|
SaveReverseProxyConfig(newProxyEndpoint)
|
||||||
ProxyType: eptype,
|
|
||||||
Rootname: targetProxyEntry.RootOrMatchingDomain,
|
|
||||||
ProxyTarget: endpoint,
|
|
||||||
UseTLS: useTLS,
|
|
||||||
SkipTlsValidation: skipTlsValidation,
|
|
||||||
RequireBasicAuth: requireBasicAuth,
|
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
|
||||||
}
|
|
||||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
|
||||||
|
|
||||||
//Update uptime monitor
|
//Update uptime monitor
|
||||||
UpdateUptimeMonitorTargets()
|
UpdateUptimeMonitorTargets()
|
||||||
@ -429,21 +420,20 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ptype, err := utils.PostPara(r, "ptype")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove the config from runtime
|
//Remove the config from runtime
|
||||||
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
|
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remove the config from file
|
//Remove the config from file
|
||||||
RemoveReverseProxyConfigFile(ep)
|
fmt.Println(ep)
|
||||||
|
err = RemoveReverseProxyConfig(ep)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//Update utm if exists
|
//Update utm if exists
|
||||||
if uptimeMonitor != nil {
|
if uptimeMonitor != nil {
|
||||||
@ -473,14 +463,8 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ptype, err := utils.GetPara(r, "ptype")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Load the target proxy object from router
|
//Load the target proxy object from router
|
||||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -502,17 +486,6 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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")
|
creds, err := utils.PostPara(r, "creds")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||||
@ -520,7 +493,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Load the target proxy object from router
|
//Load the target proxy object from router
|
||||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -570,7 +543,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
targetProxy.BasicAuthCredentials = mergedCredentials
|
targetProxy.BasicAuthCredentials = mergedCredentials
|
||||||
|
|
||||||
//Save it to file
|
//Save it to file
|
||||||
SaveReverseProxyEndpointToFile(targetProxy)
|
SaveReverseProxyConfig(targetProxy)
|
||||||
|
|
||||||
//Replace runtime configuration
|
//Replace runtime configuration
|
||||||
targetProxy.UpdateToRuntime()
|
targetProxy.UpdateToRuntime()
|
||||||
@ -593,14 +566,8 @@ func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ptype, err := utils.GetPara(r, "ptype")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Load the target proxy object from router
|
//Load the target proxy object from router
|
||||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -624,12 +591,6 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ptype, err := utils.PostPara(r, "ptype")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||||
@ -637,7 +598,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Load the target proxy object from router
|
//Load the target proxy object from router
|
||||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -666,7 +627,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
//Save configs to runtime and file
|
//Save configs to runtime and file
|
||||||
targetProxy.UpdateToRuntime()
|
targetProxy.UpdateToRuntime()
|
||||||
SaveReverseProxyEndpointToFile(targetProxy)
|
SaveReverseProxyConfig(targetProxy)
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@ -679,12 +640,6 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ptype, err := utils.PostPara(r, "ptype")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||||
@ -692,7 +647,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the target proxy object from router
|
// Load the target proxy object from router
|
||||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -717,7 +672,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
// Save configs to runtime and file
|
// Save configs to runtime and file
|
||||||
targetProxy.UpdateToRuntime()
|
targetProxy.UpdateToRuntime()
|
||||||
SaveReverseProxyEndpointToFile(targetProxy)
|
SaveReverseProxyConfig(targetProxy)
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@ -728,16 +683,28 @@ func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReverseProxyList(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 {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "type not defined")
|
utils.SendErrorResponse(w, "type not defined")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if eptype == "vdir" {
|
if eptype == "host" {
|
||||||
results := []*dynamicproxy.ProxyEndpoint{}
|
results := []*dynamicproxy.ProxyEndpoint{}
|
||||||
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||||
results = append(results, value.(*dynamicproxy.ProxyEndpoint))
|
thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
|
||||||
|
|
||||||
|
//Clear the auth passwords before showing to front-end
|
||||||
|
cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
|
||||||
|
for _, user := range thisEndpoint.BasicAuthCredentials {
|
||||||
|
cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{
|
||||||
|
Username: user.Username,
|
||||||
|
PasswordHash: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
thisEndpoint.BasicAuthCredentials = cleanedCredentials
|
||||||
|
results = append(results, thisEndpoint)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -745,19 +712,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
return results[i].Domain < results[j].Domain
|
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)
|
js, _ := json.Marshal(results)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
} else if eptype == "root" {
|
} else if eptype == "root" {
|
||||||
@ -881,34 +835,3 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle list of root route options
|
|
||||||
func HandleRootRouteOptionList(w http.ResponseWriter, r *http.Request) {
|
|
||||||
js, _ := json.Marshal(dynamicProxyRouter.RootRoutingOptions)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle update of the root route edge case options. See dynamicproxy/rootRoute.go
|
|
||||||
func HandleRootRouteOptionsUpdate(w http.ResponseWriter, r *http.Request) {
|
|
||||||
enableUnsetSubdomainRedirect, err := utils.PostBool(r, "unsetRedirect")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
unsetRedirectTarget, _ := utils.PostPara(r, "unsetRedirectTarget")
|
|
||||||
|
|
||||||
newRootOption := dynamicproxy.RootRoutingOptions{
|
|
||||||
EnableRedirectForUnsetRules: enableUnsetSubdomainRedirect,
|
|
||||||
UnsetRuleRedirectTarget: unsetRedirectTarget,
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamicProxyRouter.RootRoutingOptions = &newRootOption
|
|
||||||
err = newRootOption.SaveToFile()
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
||||||
|
27
src/start.go
27
src/start.go
@ -101,6 +101,18 @@ func startupSequence() {
|
|||||||
} else {
|
} else {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Start the static web server
|
||||||
|
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
||||||
|
Sysdb: sysdb,
|
||||||
|
Port: "5487", //Default Port
|
||||||
|
WebRoot: *staticWebServerRoot,
|
||||||
|
EnableDirectoryListing: true,
|
||||||
|
EnableWebDirManager: *allowWebFileManager,
|
||||||
|
})
|
||||||
|
//Restore the web server to previous shutdown state
|
||||||
|
staticWebServer.RestorePreviousState()
|
||||||
|
|
||||||
//Create a netstat buffer
|
//Create a netstat buffer
|
||||||
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -220,21 +232,6 @@ func startupSequence() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Static Web Server
|
|
||||||
|
|
||||||
Start the static web server
|
|
||||||
*/
|
|
||||||
|
|
||||||
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
|
||||||
Sysdb: sysdb,
|
|
||||||
Port: "5487", //Default Port
|
|
||||||
WebRoot: *staticWebServerRoot,
|
|
||||||
EnableDirectoryListing: true,
|
|
||||||
EnableWebDirManager: *allowWebFileManager,
|
|
||||||
})
|
|
||||||
//Restore the web server to previous shutdown state
|
|
||||||
staticWebServer.RestorePreviousState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sequence start after everything is initialized
|
// This sequence start after everything is initialized
|
||||||
|
@ -1,92 +1,119 @@
|
|||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>Set Proxy Root</h2>
|
<h2>Default Site</h2>
|
||||||
<p>The default routing point for all incoming traffics. For all routing not found in the proxy rules, request will be redirected to the proxy root server.</p>
|
<p>Default routing options for inbound traffic (previously called Proxy Root)</p>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="grouped fields">
|
||||||
<label>Proxy Root</label>
|
<label>What to show when Zoraxy is hit with an unknown Host?</label>
|
||||||
<input type="text" id="proxyRoot" onchange="checkRootRequireTLS(this.value);">
|
<div class="field">
|
||||||
<small>E.g. localhost:8080</small>
|
<div class="ui radio defaultsite checkbox">
|
||||||
</div>
|
<input type="radio" name="defaultsiteOption" checked="checked" value="webserver">
|
||||||
<div class="field">
|
<label>Internal Static Web Server<br>
|
||||||
<div class="ui checkbox">
|
<small>Check this if you prefer a more Apache / Nginx like experience</small>
|
||||||
<input type="checkbox" id="rootReqTLS">
|
</label>
|
||||||
<label>Root require TLS connection <br><small>Check this if your proxy root URL starts with https://</small></label>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio defaultsite checkbox">
|
||||||
|
<input type="radio" name="defaultsiteOption" value="proxy">
|
||||||
|
<label>Reverse Proxy Target<br>
|
||||||
|
<small>Proxy the request to a target IP / domain</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio defaultsite checkbox">
|
||||||
|
<input type="radio" name="defaultsiteOption" value="redirect">
|
||||||
|
<label>Redirect<br>
|
||||||
|
<small>Redirect the user to a new location</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio defaultsite checkbox">
|
||||||
|
<input type="radio" name="defaultsiteOption" value="notfound">
|
||||||
|
<label>Show 404 NOT FOUND<br>
|
||||||
|
<small>Respond to request with a 404 page</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui horizontal divider">OR</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="useStaticWebServer" onchange="handleUseStaticWebServerAsRoot()">
|
|
||||||
<label>Use Static Web Server as Root <br><small>Check this if you prefer a more Apache Web Server like experience</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<button class="ui basic button" onclick="setProxyRoot()"><i class="teal home icon" ></i> Update Proxy Root</button>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<div class="field">
|
|
||||||
<h4>Root Routing Options</h4>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="unsetRedirect">
|
|
||||||
<label>Enable redirect for unset subdomains <br><small>Redirect subdomain that is not found to custom domain</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui basic segment" id="unsetRedirectDomainWrapper" style="background-color: #f7f7f7; border-radius: 1em; margin-left: 2em; padding-left: 2em; display:none;">
|
|
||||||
<div style="
|
|
||||||
position: absolute;
|
|
||||||
top:0;
|
|
||||||
left: 1em;
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-left: 10px solid transparent;
|
|
||||||
border-right: 10px solid transparent;
|
|
||||||
border-bottom: 10px solid #f7f7f7;">
|
|
||||||
|
|
||||||
|
<!-- Reverse Proxy as Default Site Options -->
|
||||||
|
<div id="defaultSiteProxyOptions" class="ui basic segment advanceoptions defaultSiteOptionDetails" style="display:none; ">
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="field">
|
||||||
|
<label>Reverse Proxy Target</label>
|
||||||
|
<input type="text" id="proxyRoot" onchange="checkRootRequireTLS(this.value);">
|
||||||
|
<small>e.g. localhost:8080 / 192.168.0.100:80 / example.com</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="rootReqTLS">
|
||||||
|
<label>Reverse proxy target require TLS connection <br><small>Check this if your proxy target URL require connection with https://</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Redirect as default site Options-->
|
||||||
|
<div id="defaultSiteRedirectOptions" class="ui basic segment advanceoptions defaultSiteOptionDetails" style="display:none;"">
|
||||||
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Redirect target domain</label>
|
<label>Redirect target domain</label>
|
||||||
<div class="ui input">
|
<div class="ui input">
|
||||||
<input id="unsetRedirectDomain" type="text" placeholder="http://example.com">
|
<input id="redirectDomain" type="text" placeholder="http://example.com">
|
||||||
</div>
|
</div>
|
||||||
<small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)<br>
|
<small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)</small>
|
||||||
Leave empty for redirecting to upper level domain (e.g. notfound.example.com <i class="right arrow icon"></i> example.com)</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
<button class="ui basic button" onclick="updateRootOptions()"><i class="blue save icon" ></i> Save Root Options</button>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
|
|
||||||
|
<button class="ui basic button" onclick="setProxyRoot(this)"><i class="green checkmark icon" ></i> Apply Changes</button>
|
||||||
|
<button class="ui basic button" onclick="initRootInfo()"><i class="refresh icon" ></i> Reset</button>
|
||||||
|
<br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
var currentDefaultSiteOption = 0; //For enum see typedef.go
|
||||||
$("#advanceRootSettings").accordion();
|
$("#advanceRootSettings").accordion();
|
||||||
|
|
||||||
function handleUseStaticWebServerAsRoot(){
|
//Handle toggle events of option radio boxes
|
||||||
let useStaticWebServer = $("#useStaticWebServer")[0].checked;
|
function updateAvaibleDefaultSiteOptions(){
|
||||||
if (useStaticWebServer){
|
let selectedDefaultSite = $('input[name="defaultsiteOption"]:checked').val();
|
||||||
|
|
||||||
|
$(".defaultSiteOptionDetails").hide();
|
||||||
|
if (selectedDefaultSite == "webserver"){
|
||||||
|
//Use build in web server as target
|
||||||
let staticWebServerURL = "127.0.0.1:" + $("#webserv_listenPort").val();
|
let staticWebServerURL = "127.0.0.1:" + $("#webserv_listenPort").val();
|
||||||
$("#proxyRoot").val(staticWebServerURL);
|
$("#proxyRoot").val(staticWebServerURL);
|
||||||
$("#proxyRoot").parent().addClass("disabled");
|
$("#proxyRoot").parent().addClass("disabled");
|
||||||
$("#rootReqTLS").parent().checkbox("set unchecked");
|
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||||
$("#rootReqTLS").parent().addClass("disabled");
|
$("#rootReqTLS").parent().addClass("disabled");
|
||||||
|
currentDefaultSiteOption = 0;
|
||||||
//Check if web server is enabled. If not, ask if the user want to enable it
|
}else if (selectedDefaultSite == "proxy"){
|
||||||
/*if (!$("#webserv_enable").parent().checkbox("is checked")){
|
$("#defaultSiteProxyOptions").show();
|
||||||
confirmBox("Enable static web server now?", function(choice){
|
|
||||||
if (choice == true){
|
|
||||||
$("#webserv_enable").parent().checkbox("set checked");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}*/
|
|
||||||
}else{
|
|
||||||
$("#rootReqTLS").parent().removeClass("disabled");
|
$("#rootReqTLS").parent().removeClass("disabled");
|
||||||
$("#proxyRoot").parent().removeClass("disabled");
|
$("#proxyRoot").parent().removeClass("disabled");
|
||||||
initRootInfo();
|
currentDefaultSiteOption = 1;
|
||||||
|
}else if (selectedDefaultSite == "redirect"){
|
||||||
|
$("#defaultSiteRedirectOptions").show();
|
||||||
|
currentDefaultSiteOption = 2;
|
||||||
|
}else if (selectedDefaultSite == "notfound"){
|
||||||
|
currentDefaultSiteOption = 3;
|
||||||
|
}else{
|
||||||
|
//Unknown option
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bind events to the radio boxes
|
||||||
|
function bindDefaultSiteRadioCheckboxEvents(){
|
||||||
|
$('input[type=radio][name=defaultsiteOption]').off("change").on("change", function() {
|
||||||
|
updateAvaibleDefaultSiteOptions();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRootInfo(callback=undefined){
|
function initRootInfo(callback=undefined){
|
||||||
@ -94,6 +121,22 @@
|
|||||||
if (data == null){
|
if (data == null){
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
|
var $radios = $('input:radio[name=defaultsiteOption]');
|
||||||
|
let proxyType = data.DefaultSiteOption;
|
||||||
|
//See typedef.go for enum conversion
|
||||||
|
if (proxyType == 0){
|
||||||
|
$radios.filter('[value=webserver]').prop('checked', true);
|
||||||
|
}else if (proxyType == 1){
|
||||||
|
$radios.filter('[value=proxy]').prop('checked', true);
|
||||||
|
$("#proxyRoot").val(data.DefaultSiteValue);
|
||||||
|
}else if (proxyType == 2){
|
||||||
|
$radios.filter('[value=redirect]').prop('checked', true);
|
||||||
|
$("#redirectDomain").val(data.DefaultSiteValue);
|
||||||
|
}else if (proxyType == 3){
|
||||||
|
$radios.filter('[value=notfound]').prop('checked', true);
|
||||||
|
}
|
||||||
|
updateAvaibleDefaultSiteOptions();
|
||||||
|
|
||||||
$("#proxyRoot").val(data.Domain);
|
$("#proxyRoot").val(data.Domain);
|
||||||
checkRootRequireTLS(data.Domain);
|
checkRootRequireTLS(data.Domain);
|
||||||
}
|
}
|
||||||
@ -104,21 +147,9 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
initRootInfo(function(){
|
initRootInfo(function(){
|
||||||
updateWebServerLinkSettings();
|
bindDefaultSiteRadioCheckboxEvents();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Update the current web server port settings
|
|
||||||
function updateWebServerLinkSettings(){
|
|
||||||
isUsingStaticWebServerAsRoot(function(isUsingWebServ){
|
|
||||||
if (isUsingWebServ){
|
|
||||||
$(".webservRootDisabled").addClass("disabled");
|
|
||||||
$("#useStaticWebServer").parent().checkbox("set checked");
|
|
||||||
}else{
|
|
||||||
$(".webservRootDisabled").removeClass("disabled");
|
|
||||||
$("#useStaticWebServer").parent().checkbox("set unchecked");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUsingStaticWebServerAsRoot(callback){
|
function isUsingStaticWebServerAsRoot(callback){
|
||||||
let currentProxyRoot = $("#proxyRoot").val().trim();
|
let currentProxyRoot = $("#proxyRoot").val().trim();
|
||||||
@ -131,46 +162,11 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
//Bind event to tab switch
|
||||||
tabSwitchEventBind["setroot"] = function(){
|
tabSwitchEventBind["setroot"] = function(){
|
||||||
//On switch over to this page, update root info
|
|
||||||
updateRootSettingStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Toggle the display status of the input box for domain setting
|
|
||||||
function updateRedirectionDomainSettingInputBox(useRedirect){
|
|
||||||
if(useRedirect){
|
|
||||||
$("#unsetRedirectDomainWrapper").stop().finish().slideDown("fast");
|
|
||||||
}else{
|
|
||||||
$("#unsetRedirectDomainWrapper").stop().finish().slideUp("fast");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkCustomRedirectForUnsetSubd(){
|
|
||||||
$.get("/api/proxy/root/listOptions", function(data){
|
|
||||||
$("#unsetRedirect")[0].checked = data.EnableRedirectForUnsetRules || false;
|
|
||||||
$("#unsetRedirectDomain").val(data.UnsetRuleRedirectTarget);
|
|
||||||
updateRedirectionDomainSettingInputBox(data.EnableRedirectForUnsetRules);
|
|
||||||
|
|
||||||
//Bind event to the checkbox
|
|
||||||
$("#unsetRedirect").off("change").on("change", function(){
|
|
||||||
let useRedirect = $("#unsetRedirect")[0].checked;
|
|
||||||
updateRedirectionDomainSettingInputBox(useRedirect);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
checkCustomRedirectForUnsetSubd();
|
|
||||||
|
|
||||||
//Check if the given domain will redirect to https
|
//Check if the given domain will redirect to https
|
||||||
function checkRootRequireTLS(targetDomain){
|
function checkRootRequireTLS(targetDomain){
|
||||||
@ -193,14 +189,15 @@
|
|||||||
}else if (data == "http"){
|
}else if (data == "http"){
|
||||||
$("#rootReqTLS").parent().checkbox("set unchecked");
|
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set the new proxy root option
|
//Set the new proxy root option
|
||||||
function setProxyRoot(){
|
function setProxyRoot(btn=undefined){
|
||||||
|
if (btn != undefined){
|
||||||
|
$(btn).addClass("disabled");
|
||||||
|
}
|
||||||
var newpr = $("#proxyRoot").val();
|
var newpr = $("#proxyRoot").val();
|
||||||
if (newpr.trim() == ""){
|
if (newpr.trim() == ""){
|
||||||
$("#proxyRoot").parent().addClass('error');
|
$("#proxyRoot").parent().addClass('error');
|
||||||
@ -211,10 +208,36 @@
|
|||||||
|
|
||||||
var rootReqTls = $("#rootReqTLS")[0].checked;
|
var rootReqTls = $("#rootReqTLS")[0].checked;
|
||||||
|
|
||||||
|
//proxy mode or redirect mode, check for input values
|
||||||
|
var defaultSiteValue = "";
|
||||||
|
if (currentDefaultSiteOption == 1){
|
||||||
|
if ($("#proxyRoot").val().trim() == ""){
|
||||||
|
$("#proxyRoot").parent().addClass("error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
defaultSiteValue = $("#proxyRoot").val().trim();
|
||||||
|
$("#proxyRoot").parent().removeClass("error");
|
||||||
|
|
||||||
|
}else if (currentDefaultSiteOption == 2){
|
||||||
|
if ($("#redirectDomain").val().trim() == ""){
|
||||||
|
$("#redirectDomain").parent().addClass("error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
defaultSiteValue = $("#redirectDomain").val().trim();
|
||||||
|
$("#redirectDomain").parent().removeClass("error");
|
||||||
|
}
|
||||||
|
|
||||||
//Create the endpoint by calling add
|
//Create the endpoint by calling add
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/add",
|
url: "/api/proxy/add",
|
||||||
data: {"type": "root", tls: rootReqTls, ep: newpr},
|
data: {
|
||||||
|
"type": "root",
|
||||||
|
"tls": rootReqTls,
|
||||||
|
"ep": newpr,
|
||||||
|
"defaultSiteOpt": currentDefaultSiteOption,
|
||||||
|
"defaultSiteVal":defaultSiteValue,
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
msgbox(data.error, false, 5000);
|
msgbox(data.error, false, 5000);
|
||||||
@ -231,37 +254,20 @@
|
|||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
//Update the checkbox
|
//Update the checkbox
|
||||||
updateWebServerLinkSettings();
|
|
||||||
msgbox("Proxy Root Updated");
|
msgbox("Proxy Root Updated");
|
||||||
}, 1000);
|
}, 100);
|
||||||
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (btn != undefined){
|
||||||
|
$(btn).removeClass("disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
</script>
|
@ -1,119 +1,114 @@
|
|||||||
<div class="ui stackable grid">
|
<!-- Proxy Create Form-->
|
||||||
|
<style>
|
||||||
|
.rulesInstructions{
|
||||||
|
background: var(--theme_background) !important;
|
||||||
|
color: var(--theme_lgrey);
|
||||||
|
border-radius: 1em !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="standardContainer">
|
||||||
|
<div class="ui stackable grid">
|
||||||
<div class="ten wide column">
|
<div class="ten wide column">
|
||||||
<div class="standardContainer">
|
<div class="ui basic segment" style="border-radius: 1em; padding: 1em !important;">
|
||||||
<div class="ui basic segment" style="margin-top: 1em;">
|
<h2>New Proxy Rule</h2>
|
||||||
<h2>New Proxy Rule</h2>
|
<p>You can add more proxy rules to support more site via domain / subdomains</p>
|
||||||
<p>You can create a proxy endpoing by subdomain or virtual directories</p>
|
<div class="ui form">
|
||||||
<div class="ui form">
|
<div class="field">
|
||||||
<div class="field">
|
<label>Matching Keyword / Domain</label>
|
||||||
<label>Proxy Type</label>
|
<input type="text" id="rootname" placeholder="mydomain.com">
|
||||||
<div class="ui selection dropdown">
|
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com</small>
|
||||||
<input type="hidden" id="ptype" value="subd" onchange="handleProxyTypeOptionChange(this.value)">
|
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
<div class="default text">Proxy Type</div>
|
|
||||||
<div class="menu">
|
|
||||||
<div class="item" data-value="subd">Sub-domain</div>
|
|
||||||
<div class="item" data-value="vdir">Virtual Directory</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Subdomain Matching Keyword / Virtual Directory Name</label>
|
|
||||||
<input type="text" id="rootname" placeholder="s1.mydomain.com">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Target IP Address or Domain Name with port</label>
|
|
||||||
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
|
|
||||||
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="reqTls">
|
|
||||||
<label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Advance configs -->
|
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
|
||||||
<div id="advanceProxyRules" class="ui fluid accordion">
|
|
||||||
<div class="title">
|
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
Advance Settings
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p></p>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="skipTLSValidation">
|
|
||||||
<label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="bypassGlobalTLS">
|
|
||||||
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui checkbox">
|
|
||||||
<input type="checkbox" id="requireBasicAuth">
|
|
||||||
<label>Require Basic Auth<br><small>Require client to login in order to view the page</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="basicAuthCredentials" class="field">
|
|
||||||
<p>Enter the username and password for allowing them to access this proxy endpoint</p>
|
|
||||||
<table class="ui very basic celled table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Username</th>
|
|
||||||
<th>Password</th>
|
|
||||||
<th>Remove</th>
|
|
||||||
</tr></thead>
|
|
||||||
<tbody id="basicAuthCredentialTable">
|
|
||||||
<tr>
|
|
||||||
<td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="three small fields credentialEntry">
|
|
||||||
<div class="field">
|
|
||||||
<input id="basicAuthCredUsername" type="text" placeholder="Username" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<input id="basicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<button class="ui basic button" onclick="addCredentials();"><i class="blue add icon"></i> Add Credential</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<button class="ui basic button" onclick="newProxyEndpoint();"><i class="blue add icon"></i> Create Endpoint</button>
|
|
||||||
<br><br>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Target IP Address or Domain Name with port</label>
|
||||||
|
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
|
||||||
|
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="reqTls">
|
||||||
|
<label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Advance configs -->
|
||||||
|
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
||||||
|
<div id="advanceProxyRules" class="ui fluid accordion">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advance Settings
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p></p>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="skipTLSValidation">
|
||||||
|
<label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="bypassGlobalTLS">
|
||||||
|
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="requireBasicAuth">
|
||||||
|
<label>Require Basic Auth<br><small>Require client to login in order to view the page</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="basicAuthCredentials" class="field">
|
||||||
|
<p>Enter the username and password for allowing them to access this proxy endpoint</p>
|
||||||
|
<table class="ui very basic celled table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Password</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody id="basicAuthCredentialTable">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3"><i class="ui green circle check icon"></i> No Entered Credential</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="three small fields credentialEntry">
|
||||||
|
<div class="field">
|
||||||
|
<input id="basicAuthCredUsername" type="text" placeholder="Username" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<input id="basicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="ui basic button" onclick="addCredentials();"><i class="blue add icon"></i> Add Credential</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button class="ui basic button" onclick="newProxyEndpoint();"><i class="blue add icon"></i> Create Endpoint</button>
|
||||||
|
<br><br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="six wide column">
|
<div class="six wide column">
|
||||||
<div class="ui basic segment" style="height: 100%; background-color: var(--theme_grey); color: var(--theme_lgrey);">
|
<div class="ui basic segment rulesInstructions">
|
||||||
<br>
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Domain</span><br>
|
||||||
<span style="font-size: 1.2em; font-weight: 300;">Subdomain</span><br>
|
Example of domain matching keyword:<br>
|
||||||
Example of subdomain matching keyword:<br>
|
<code>arozos.com</code> <br>Any acess requesting arozos.com will be proxy to the IP address below<br>
|
||||||
<code>s1.arozos.com</code> <br>(Any access starting with s1.arozos.com will be proxy to the IP address below)<br>
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<span style="font-size: 1.2em; font-weight: 300;">Virtual Directory</span><br>
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Subdomain</span><br>
|
||||||
Example of virtual directory name: <br>
|
Example of subdomain matching keyword:<br>
|
||||||
<code>/s1/home/</code> <br>(Any access to {this_server}/s1/home/ will be proxy to the IP address below)<br>
|
<code>s1.arozos.com</code> <br>Any request starting with s1.arozos.com will be proxy to the IP address below<br>
|
||||||
You can also ignore the tailing slash for wildcard like usage.<br>
|
<div class="ui divider"></div>
|
||||||
<code>/s1/room-</code> <br>Any access to {this_server}/s1/classroom_* will be proxied, for example: <br>
|
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Wildcard</span><br>
|
||||||
|
Example of wildcard matching keyword:<br>
|
||||||
|
<code>*.arozos.com</code> <br>Any request with a host name matching *.arozos.com will be proxy to the IP address below. Here are some examples.<br>
|
||||||
<div class="ui list">
|
<div class="ui list">
|
||||||
<div class="item"><code>/s1/room-101</code></div>
|
<div class="item"><code>www.arozos.com</code></div>
|
||||||
<div class="item"><code>/s1/room-102/</code></div>
|
<div class="item"><code>foo.bar.arozos.com</code></div>
|
||||||
<div class="item"><code>/s1/room-103/map.txt</code></div>
|
</div>
|
||||||
</div><br>
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -124,7 +119,6 @@
|
|||||||
|
|
||||||
//New Proxy Endpoint
|
//New Proxy Endpoint
|
||||||
function newProxyEndpoint(){
|
function newProxyEndpoint(){
|
||||||
var type = $("#ptype").val();
|
|
||||||
var rootname = $("#rootname").val();
|
var rootname = $("#rootname").val();
|
||||||
var proxyDomain = $("#proxyDomain").val();
|
var proxyDomain = $("#proxyDomain").val();
|
||||||
var useTLS = $("#reqTls")[0].checked;
|
var useTLS = $("#reqTls")[0].checked;
|
||||||
@ -132,20 +126,6 @@
|
|||||||
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
||||||
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
||||||
|
|
||||||
if (type === "vdir") {
|
|
||||||
if (!rootname.startsWith("/")) {
|
|
||||||
rootname = "/" + rootname
|
|
||||||
$("#rootname").val(rootname);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (!isSubdomainDomain(rootname)){
|
|
||||||
//This doesn't seems like a subdomain
|
|
||||||
if (!confirm(rootname + " does not looks like a subdomain. Continue anyway?")){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rootname.trim() == ""){
|
if (rootname.trim() == ""){
|
||||||
$("#rootname").parent().addClass("error");
|
$("#rootname").parent().addClass("error");
|
||||||
return
|
return
|
||||||
@ -164,7 +144,7 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/add",
|
url: "/api/proxy/add",
|
||||||
data: {
|
data: {
|
||||||
type: type,
|
type: "host",
|
||||||
rootname: rootname,
|
rootname: rootname,
|
||||||
tls: useTLS,
|
tls: useTLS,
|
||||||
ep: proxyDomain,
|
ep: proxyDomain,
|
||||||
@ -178,8 +158,6 @@
|
|||||||
msgbox(data.error, false, 5000);
|
msgbox(data.error, false, 5000);
|
||||||
}else{
|
}else{
|
||||||
//OK
|
//OK
|
||||||
listVdirs();
|
|
||||||
listSubd();
|
|
||||||
|
|
||||||
|
|
||||||
//Clear old data
|
//Clear old data
|
||||||
@ -189,7 +167,7 @@
|
|||||||
updateTable();
|
updateTable();
|
||||||
|
|
||||||
//Check if it is a new subdomain and TLS enabled
|
//Check if it is a new subdomain and TLS enabled
|
||||||
if (type == "subd" && $("#tls").checkbox("is checked")){
|
if ($("#tls").checkbox("is checked")){
|
||||||
confirmBox("Request new SSL Cert for this subdomain?", function(choice){
|
confirmBox("Request new SSL Cert for this subdomain?", function(choice){
|
||||||
if (choice == true){
|
if (choice == true){
|
||||||
//Load the prefer CA from TLS page
|
//Load the prefer CA from TLS page
|
||||||
@ -214,23 +192,14 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleProxyTypeOptionChange(newType){
|
|
||||||
if (newType == "subd"){
|
|
||||||
$("#bypassGlobalTLS").parent().removeClass("disabled");
|
|
||||||
}else if (newType == "vdir"){
|
|
||||||
$("#bypassGlobalTLS").parent().addClass("disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Generic functions for delete rp endpoints
|
//Generic functions for delete rp endpoints
|
||||||
function deleteEndpoint(ptype, epoint){
|
function deleteEndpoint(ptype, epoint){
|
||||||
if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
|
if (confirm("Confirm remove proxy for :" + epoint + "?")){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/del",
|
url: "/api/proxy/del",
|
||||||
data: {ep: epoint, ptype: ptype},
|
data: {ep: epoint, },
|
||||||
success: function(){
|
success: function(){
|
||||||
listVdirs();
|
listProxyEndpoints();
|
||||||
listSubd();
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -330,14 +299,6 @@
|
|||||||
updateTable();
|
updateTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Check if a string is a valid subdomain
|
|
||||||
function isSubdomainDomain(str) {
|
|
||||||
const regex = /^(localhost|[a-z0-9]+([\-.]{1}[a-z0-9]+)*\.[a-z]{2,}|[a-z0-9]+([\-.]{1}[a-z0-9]+)*\.[a-z]{2,}\.)$/i;
|
|
||||||
return regex.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Inline editor for subd.html and vdir.html
|
Inline editor for subd.html and vdir.html
|
||||||
*/
|
*/
|
||||||
@ -411,7 +372,7 @@
|
|||||||
<button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
|
<button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
|
||||||
<button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
|
<button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
|
||||||
`);
|
`);
|
||||||
}else if (datatype == "inbound" && payload.ProxyType == 0){
|
}else if (datatype == "inbound"){
|
||||||
let originalContent = $(column).html();
|
let originalContent = $(column).html();
|
||||||
column.empty().append(`${originalContent}
|
column.empty().append(`${originalContent}
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
@ -429,9 +390,8 @@
|
|||||||
$("#" + endpointType).find(".editBtn").addClass("disabled");
|
$("#" + endpointType).find(".editBtn").addClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
function exitProxyInlineEdit(){
|
function exitProxyInlineEdit(endpointType){
|
||||||
listSubd();
|
listProxyEndpoints();
|
||||||
listVdirs();
|
|
||||||
$("#" + endpointType).find(".editBtn").removeClass("disabled");
|
$("#" + endpointType).find(".editBtn").removeClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,13 +401,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var epttype = $(row).attr("class");
|
var epttype = "host";
|
||||||
if (epttype == "subdEntry"){
|
|
||||||
epttype = "subd";
|
|
||||||
}else if (epttype == "vdirEntry"){
|
|
||||||
epttype = "vdir";
|
|
||||||
}
|
|
||||||
|
|
||||||
let newDomain = $(row).find(".Domain").val();
|
let newDomain = $(row).find(".Domain").val();
|
||||||
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
||||||
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
||||||
@ -473,11 +427,7 @@
|
|||||||
msgbox(data.error, false, 6000);
|
msgbox(data.error, false, 6000);
|
||||||
}else{
|
}else{
|
||||||
msgbox("Proxy endpoint updated");
|
msgbox("Proxy endpoint updated");
|
||||||
if (epttype == "subd"){
|
listProxyEndpoints();
|
||||||
listSubd();
|
|
||||||
}else if (epttype == "vdir"){
|
|
||||||
listVdirs();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="six wide column statisticWrapper">
|
<div class="six wide column statisticWrapper">
|
||||||
<div class="ui greybackground statustab segment">
|
<div class="ui statustab segment">
|
||||||
<h5 class="ui header">
|
<h5 class="ui header">
|
||||||
<i class="exchange icon"></i>
|
<i class="exchange icon"></i>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h5 class="ui header">
|
<h5 class="ui header" style="margin-top: 0px;">
|
||||||
<i class="arrows alternate horizontal icon"></i>
|
<i class="arrows alternate horizontal icon"></i>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span id="forwardtype"></span>
|
<span id="forwardtype"></span>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</h5>
|
</h5>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h5 class="ui header">
|
<h5 class="ui header" style="margin-top: 0px;">
|
||||||
<i class="map marker alternate icon"></i>
|
<i class="map marker alternate icon"></i>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span id="country"></span>
|
<span id="country"></span>
|
||||||
@ -51,20 +51,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="standardContainer" style="padding-bottom: 0 !important;">
|
||||||
<div id="networkActWrapper" class="standardContainer" style="position: relative; margin-top: 1em;">
|
<!-- Power Buttons-->
|
||||||
|
<button id="startbtn" class="ui basic button" onclick="startService();"><i class="ui green arrow alternate circle up icon"></i> Start Service</button>
|
||||||
|
<button id="stopbtn" class="ui basic notloopbackOnly disabled button" onclick="stopService();"><i class="ui red minus circle icon"></i> Stop Service</button>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h4>Network Status</h4>
|
||||||
|
<p>Overall Network I/O in Current Host Server</p>
|
||||||
|
</div>
|
||||||
|
<div id="networkActWrapper" class="standardContainer" style="position: relative;">
|
||||||
<canvas id="networkActivity"></canvas>
|
<canvas id="networkActivity"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<div id="networkActivityPlaceHolder">
|
<div id="networkActivityPlaceHolder">
|
||||||
<p style="opacity: 0.5;"><i class="ui pause icon"></i> Graph Render Paused</p>
|
<p style="opacity: 0.5;"> Graph Render Paused</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<h4>Basic Settings</h4>
|
<div class="ui divider"></div>
|
||||||
|
<h4>Global Settings</h4>
|
||||||
<p>Inbound Port (Port to be proxied)</p>
|
<p>Inbound Port (Port to be proxied)</p>
|
||||||
<div class="ui action fluid notloopbackOnly input">
|
<div class="ui action fluid notloopbackOnly input">
|
||||||
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
|
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
|
||||||
<button class="ui basic green notloopbackOnly button" onclick="handlePortChange();">Apply</button>
|
<button class="ui basic notloopbackOnly button" onclick="handlePortChange();"><i class="ui green checkmark icon"></i> Apply</button>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div id="tls" class="ui toggle notloopbackOnly checkbox">
|
<div id="tls" class="ui toggle notloopbackOnly checkbox">
|
||||||
@ -100,10 +107,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
<button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
|
|
||||||
<button id="stopbtn" class="ui red notloopbackOnly disabled button" onclick="stopService();">Stop Service</button>
|
|
||||||
<div id="rploopbackWarning" class="ui segment" style="display:none;">
|
<div id="rploopbackWarning" class="ui segment" style="display:none;">
|
||||||
<b><i class="yellow warning icon"></i> Loopback Routing Warning</b><br>
|
<b><i class="yellow warning icon"></i> Loopback Routing Warning</b><br>
|
||||||
<small>This management interface is a loopback proxied service. <br>If you want to shutdown the reverse proxy server, please remove the proxy rule for the management interface and refresh.</small>
|
<small>This management interface is a loopback proxied service. <br>If you want to shutdown the reverse proxy server, please remove the proxy rule for the management interface and refresh.</small>
|
||||||
@ -114,7 +117,7 @@
|
|||||||
<div class="ui two column stackable grid">
|
<div class="ui two column stackable grid">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p>Visitor Counts</p>
|
<p>Visitor Counts</p>
|
||||||
<table class="ui unstackable inverted celled table">
|
<table class="ui unstackable very basic celled table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Country ISO Code</th>
|
<th>Country ISO Code</th>
|
||||||
@ -130,7 +133,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p>Proxy Request Types</p>
|
<p>Proxy Request Types</p>
|
||||||
<table class="ui unstackable inverted celled table">
|
<table class="ui unstackable very basic celled table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Proxy Type</th>
|
<th>Proxy Type</th>
|
||||||
@ -175,13 +178,13 @@
|
|||||||
}
|
}
|
||||||
$("#serverstatus").addClass("green");
|
$("#serverstatus").addClass("green");
|
||||||
$("#statusTitle").text("Online");
|
$("#statusTitle").text("Online");
|
||||||
$("#rpStatusIcon").attr("class", "green circle check icon");
|
$("#rpStatusIcon").attr("class", "white circle check icon");
|
||||||
$("#statusText").text("Serving request on port: " + data.Option.Port);
|
$("#statusText").text("Serving request on port: " + data.Option.Port);
|
||||||
}else{
|
}else{
|
||||||
$("#startbtn").removeClass("disabled");
|
$("#startbtn").removeClass("disabled");
|
||||||
$("#stopbtn").addClass("disabled");
|
$("#stopbtn").addClass("disabled");
|
||||||
$("#statusTitle").text("Offline");
|
$("#statusTitle").text("Offline");
|
||||||
$("#rpStatusIcon").attr("class", "black circle times icon")
|
$("#rpStatusIcon").attr("class", "yellow moon icon")
|
||||||
$("#statusText").text("Reverse proxy server is offline");
|
$("#statusText").text("Reverse proxy server is offline");
|
||||||
$("#serverstatus").removeClass("green");
|
$("#serverstatus").removeClass("green");
|
||||||
}
|
}
|
||||||
@ -565,6 +568,7 @@
|
|||||||
options: {
|
options: {
|
||||||
animation: false,
|
animation: false,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
bezierCurve: true,
|
||||||
tooltips: {enabled: false},
|
tooltips: {enabled: false},
|
||||||
hover: {mode: null},
|
hover: {mode: null},
|
||||||
//stepped: 'middle',
|
//stepped: 'middle',
|
||||||
@ -606,18 +610,18 @@
|
|||||||
{
|
{
|
||||||
label: 'Inbound',
|
label: 'Inbound',
|
||||||
data: rxValues,
|
data: rxValues,
|
||||||
borderColor: "#4d9dd9",
|
borderColor: "#484bb8",
|
||||||
borderWidth: 2,
|
borderWidth: 1,
|
||||||
backgroundColor: 'rgba(77, 157, 217, 0.2)',
|
backgroundColor: 'rgba(72, 75, 184, 0.2)',
|
||||||
fill: true,
|
fill: true,
|
||||||
pointStyle: false,
|
pointStyle: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Outbound',
|
label: 'Outbound',
|
||||||
data: txValues,
|
data: txValues,
|
||||||
borderColor: '#ffe32b',
|
borderColor: '#02a9c1',
|
||||||
borderWidth: 2,
|
borderWidth: 1,
|
||||||
backgroundColor: 'rgba(255, 227, 43, 0.2)',
|
backgroundColor: 'rgba(2, 169, 193, 0.2)',
|
||||||
fill: true,
|
fill: true,
|
||||||
pointStyle: false,
|
pointStyle: false,
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>Subdomain</h2>
|
<h2>HTTP Proxy</h2>
|
||||||
<p>Subdomains are a way to organize and identify different sections of a website or domain. They are essentially a prefix to the main domain name, separated by a dot. <br>For example, in the domain "blog.example.com," "blog" is the subdomain.</p>
|
<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>
|
||||||
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
||||||
<table class="ui celled sortable unstackable compact table">
|
<table class="ui celled sortable unstackable compact table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Matching Domain</th>
|
<th>Host</th>
|
||||||
<th>Proxy To</th>
|
<th>Destination</th>
|
||||||
|
<th>Virtual Directory</th>
|
||||||
<th>Basic Auth</th>
|
<th>Basic Auth</th>
|
||||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -18,13 +19,13 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<button class="ui icon right floated basic button" onclick="listSubd();"><i class="green refresh icon"></i> Refresh</button>
|
<button class="ui icon right floated basic button" onclick="listProxyEndpoints();"><i class="green refresh icon"></i> Refresh</button>
|
||||||
<br><br>
|
<br><br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function listSubd(){
|
function listProxyEndpoints(){
|
||||||
$.get("/api/proxy/list?type=subd", function(data){
|
$.get("/api/proxy/list?type=host", function(data){
|
||||||
$("#subdList").html(``);
|
$("#subdList").html(``);
|
||||||
if (data.error !== undefined){
|
if (data.error !== undefined){
|
||||||
$("#subdList").append(`<tr>
|
$("#subdList").append(`<tr>
|
||||||
@ -32,7 +33,7 @@
|
|||||||
</tr>`);
|
</tr>`);
|
||||||
}else if (data.length == 0){
|
}else if (data.length == 0){
|
||||||
$("#subdList").append(`<tr>
|
$("#subdList").append(`<tr>
|
||||||
<td data-label="" colspan="3"><i class="checkmark icon"></i> No Subdomain Proxy Record</td>
|
<td data-label="" colspan="3"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
}else{
|
}else{
|
||||||
data.forEach(subd => {
|
data.forEach(subd => {
|
||||||
@ -45,9 +46,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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">
|
$("#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></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">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 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" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||||
@ -61,6 +74,6 @@
|
|||||||
|
|
||||||
//Bind on tab switch events
|
//Bind on tab switch events
|
||||||
tabSwitchEventBind["subd"] = function(){
|
tabSwitchEventBind["subd"] = function(){
|
||||||
listSubd();
|
listProxyEndpoints();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
BIN
src/web/img/axiom-pattern.png
Normal file
BIN
src/web/img/axiom-pattern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 307 B |
@ -36,18 +36,24 @@
|
|||||||
<a class="item active" tag="status">
|
<a class="item active" tag="status">
|
||||||
<i class="simplistic info circle icon"></i>Status
|
<i class="simplistic info circle icon"></i>Status
|
||||||
</a>
|
</a>
|
||||||
|
<a class="item" tag="setroot">
|
||||||
|
<i class="simplistic home icon"></i> Default Site
|
||||||
|
</a>
|
||||||
|
<div class="ui divider menudivider">Reverse Proxy</div>
|
||||||
|
<a class="item" tag="subd">
|
||||||
|
<i class="simplistic sitemap icon"></i> HTTP Proxy
|
||||||
|
</a>
|
||||||
<a class="item" tag="vdir">
|
<a class="item" tag="vdir">
|
||||||
<i class="simplistic folder icon"></i> Virtual Directory
|
<i class="simplistic folder icon"></i> Virtual Directory
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="subd">
|
|
||||||
<i class="simplistic sitemap icon"></i> Subdomain Proxy
|
|
||||||
</a>
|
|
||||||
<a class="item" tag="rules">
|
<a class="item" tag="rules">
|
||||||
<i class="simplistic plus square icon"></i> Create Proxy Rules
|
<i class="simplistic plus square icon"></i> Create Proxy Rules
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="setroot">
|
<a class="item" tag="tcpprox">
|
||||||
<i class="simplistic home icon"></i> Set Proxy Root
|
<i class="simplistic exchange icon"></i> TCP Proxy
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<div class="ui divider menudivider">Access & Connections</div>
|
<div class="ui divider menudivider">Access & Connections</div>
|
||||||
<a class="item" tag="cert">
|
<a class="item" tag="cert">
|
||||||
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||||
@ -65,16 +71,14 @@
|
|||||||
<a class="item" tag="zgrok">
|
<a class="item" tag="zgrok">
|
||||||
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="tcpprox">
|
|
||||||
<i class="simplistic exchange icon"></i> TCP Proxy
|
<div class="ui divider menudivider">Hosting</div>
|
||||||
</a>
|
|
||||||
<div class="ui divider menudivider">Others</div>
|
|
||||||
<a class="item" tag="webserv">
|
<a class="item" tag="webserv">
|
||||||
<i class="simplistic globe icon"></i> Static Web Server
|
<i class="simplistic globe icon"></i> Static Web Server
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="utm">
|
|
||||||
<i class="simplistic time icon"></i> Uptime Monitor
|
<div class="ui divider menudivider">Others</div>
|
||||||
</a>
|
|
||||||
<a class="item" tag="networktool">
|
<a class="item" tag="networktool">
|
||||||
<i class="simplistic terminal icon"></i> Network Tools
|
<i class="simplistic terminal icon"></i> Network Tools
|
||||||
</a>
|
</a>
|
||||||
@ -235,7 +239,7 @@
|
|||||||
$.get("/api/auth/logout", function(response) {
|
$.get("/api/auth/logout", function(response) {
|
||||||
if (response === "OK") {
|
if (response === "OK") {
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
window.location.href = "/";
|
window.location.href = "/login.html";
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -7,48 +7,13 @@
|
|||||||
<link rel="icon" type="image/png" href="./favicon.png" />
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||||
<title>Login | Zoraxy</title>
|
<title>Login | Zoraxy</title>
|
||||||
<link rel="stylesheet" href="script/semantic/semantic.min.css">
|
<link rel="stylesheet" href="script/semantic/semantic.min.css">
|
||||||
|
<link href="script/aos.css" rel="stylesheet">
|
||||||
|
<script src="script/aos.js"></script>
|
||||||
<script type="application/javascript" src="script/jquery-3.6.0.min.js"></script>
|
<script type="application/javascript" src="script/jquery-3.6.0.min.js"></script>
|
||||||
<script type="application/javascript" src="script/semantic/semantic.min.js"></script>
|
<script type="application/javascript" src="script/semantic/semantic.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: rgb(245,245,245);
|
background: linear-gradient(60deg, rgba(84,58,183,1) 0%, rgba(0,172,193,1) 100%);
|
||||||
background: linear-gradient(28deg, rgba(245,245,245,1) 63%, rgba(255,255,255,1) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.background{
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
opacity: 0.8;
|
|
||||||
z-index: -99;
|
|
||||||
background-image: url("img/public/bg.jpg");
|
|
||||||
background-size: auto 100%;
|
|
||||||
background-position: right top;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin:auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loginForm{
|
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
|
||||||
width: 25em;
|
|
||||||
margin-left: 10em;
|
|
||||||
margin-top: 0 !important;
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 550px) {
|
|
||||||
/* CSS rules here for screens lower than 750px */
|
|
||||||
#loginForm{
|
|
||||||
width: calc(100% - 4em);
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#errmsg{
|
#errmsg{
|
||||||
@ -65,13 +30,97 @@
|
|||||||
.ui.fluid.button.registerOnly{
|
.ui.fluid.button.registerOnly{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loginForm {
|
||||||
|
border-radius: 1em;
|
||||||
|
width: 25em;
|
||||||
|
height: 450px;
|
||||||
|
position: absolute; /*Can also be `fixed`*/
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
/*Solves a problem in which the content is being cut when the div is smaller than its' wrapper:*/
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wavebase {
|
||||||
|
position:fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height:5vh;
|
||||||
|
text-align:center;
|
||||||
|
padding-top: 1em;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Waves CSS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#wavesWrapper{
|
||||||
|
position: fixed;
|
||||||
|
bottom: 5vh;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waves {
|
||||||
|
position:relative;
|
||||||
|
width: 100%;
|
||||||
|
height:15vh;
|
||||||
|
margin-bottom:-7px; /*Fix for safari gap*/
|
||||||
|
min-height:100px;
|
||||||
|
max-height:150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.parallax > use {
|
||||||
|
animation: move-forever 25s cubic-bezier(.55,.5,.45,.5) infinite;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(1) {
|
||||||
|
animation-delay: -8s;
|
||||||
|
animation-duration: 28s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(2) {
|
||||||
|
animation-delay: -12s;
|
||||||
|
animation-duration: 40s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(3) {
|
||||||
|
animation-delay: -16s;
|
||||||
|
animation-duration: 52s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(4) {
|
||||||
|
animation-delay: -20s;
|
||||||
|
animation-duration: 80s;
|
||||||
|
}
|
||||||
|
@keyframes move-forever {
|
||||||
|
0% {
|
||||||
|
transform: translate3d(-90px,0,0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate3d(85px,0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*Shrinking for mobile*/
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.waves {
|
||||||
|
height:40px;
|
||||||
|
min-height:40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="background"></div>
|
<div id="loginForm" class="ui middle aligned center aligned grid" data-aos="fade-up">
|
||||||
<div id="loginForm" class="ui middle aligned center aligned grid">
|
<div class="column" style="padding-top: 0 !important;">
|
||||||
<div class="column">
|
<form class="ui large form content">
|
||||||
<form class="ui large form">
|
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<img class="ui fluid image" src="img/public/logo.svg" style="pointer-events:none;">
|
<img class="ui fluid image" src="img/public/logo.svg" style="pointer-events:none;">
|
||||||
<p class="registerOnly">Account Setup</p>
|
<p class="registerOnly">Account Setup</p>
|
||||||
@ -99,19 +148,37 @@
|
|||||||
<label>Remember Me</label>
|
<label>Remember Me</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="loginbtn" class="ui fluid basic blue button loginOnly">Login</div>
|
<div id="loginbtn" class="ui fluid basic button loginOnly"> <i class="ui blue sign-in icon"></i> Login</div>
|
||||||
<div id="regsiterbtn" class="ui fluid basic blue button registerOnly">Create</div>
|
<div id="regsiterbtn" class="ui fluid basic button registerOnly"><i class="ui green checkmark icon"></i> Confirm</div>
|
||||||
<div id="errmsg"></div>
|
<div id="errmsg"></div>
|
||||||
<div id="forgetPassword" class="field loginOnly" style="text-align: right;">
|
<div id="forgetPassword" class="field loginOnly" style="text-align: right; margin-top: 2em;">
|
||||||
<a href="#" onclick="sendResetAccountEmail();">Forget Password</a>
|
<a href="#" onclick="sendResetAccountEmail();">Forget Password</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
|
||||||
<small>Proudly powered by Zoraxy</small>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="wavesWrapper">
|
||||||
|
<!-- CSS waves-->
|
||||||
|
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
||||||
|
<defs>
|
||||||
|
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
||||||
|
</defs>
|
||||||
|
<g class="parallax">
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(255,255,255,0.7" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(255,255,255,0.5)" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(255,255,255,0.3)" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="7" fill="#fff" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="wavebase">
|
||||||
|
<p>Proudly powered by <a href="https://zoraxy.arozos.com" target="_blank">Zoraxy</a></p>
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
AOS.init();
|
||||||
|
|
||||||
var redirectionAddress = "/";
|
var redirectionAddress = "/";
|
||||||
var loginAddress = "/api/auth/login";
|
var loginAddress = "/api/auth/login";
|
||||||
$(".checkbox").checkbox();
|
$(".checkbox").checkbox();
|
||||||
|
112
src/web/main.css
112
src/web/main.css
@ -7,6 +7,7 @@
|
|||||||
--theme_green: #3c9c63;
|
--theme_green: #3c9c63;
|
||||||
--theme_fcolor: #979797;
|
--theme_fcolor: #979797;
|
||||||
--theme_advance: #f8f8f9;
|
--theme_advance: #f8f8f9;
|
||||||
|
--theme_background: linear-gradient(60deg, rgb(84, 58, 183) 0%, rgb(0, 172, 193) 100%);
|
||||||
}
|
}
|
||||||
body{
|
body{
|
||||||
background-color:#f6f6f6;
|
background-color:#f6f6f6;
|
||||||
@ -31,14 +32,14 @@ body{
|
|||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
padding-left: 1.2em;
|
padding-left: 1.2em;
|
||||||
padding-right: 1.2em;
|
padding-right: 1.2em;
|
||||||
background-color: #f5f5f5;
|
background-color: #ffffff;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
border-bottom: 1px solid rgb(226, 226, 226);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
box-shadow: 0px 1px 5px 0px rgba(38,38,38,0.26);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menubar .logo{
|
.menubar .logo{
|
||||||
@ -82,13 +83,21 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.serverstatusWrapper{
|
.serverstatusWrapper{
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statisticWrapper{
|
.statisticWrapper{
|
||||||
|
margin-top: 1em;
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
padding-right: 1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.statisticWrapper .statustab{
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Message Box */
|
/* Message Box */
|
||||||
#messageBox{
|
#messageBox{
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -204,14 +213,6 @@ body{
|
|||||||
|
|
||||||
|
|
||||||
@media screen and (min-width: 750px) {
|
@media screen and (min-width: 750px) {
|
||||||
#serverstatus{
|
|
||||||
border-top-left-radius: 1em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greybackground.statustab{
|
|
||||||
border-top-right-radius: 1em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standardContainer{
|
.standardContainer{
|
||||||
padding-left: 2.4em;
|
padding-left: 2.4em;
|
||||||
padding-right: 2.4em;
|
padding-right: 2.4em;
|
||||||
@ -221,7 +222,18 @@ body{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 750px) {
|
@media screen and (max-width: 748px) {
|
||||||
|
#serverstatus{
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.statisticWrapper .statustab{
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -257,19 +269,10 @@ body{
|
|||||||
padding: 0em;
|
padding: 0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.grid > .stackable.stackable.row > .column, .ui.stackable.grid > .column.grid > .column, .ui.stackable.grid > .column.row > .column, .ui.stackable.grid > .column:not(.row), .ui.stackable.grid > .row > .column, .ui.stackable.grid > .row > .wide.column, .ui.stackable.grid > .wide.column.serverstatusWrapper {
|
|
||||||
padding: 0rem 0rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#serverstatus.green{
|
#serverstatus.green{
|
||||||
border-bottom: 0px solid transparent !important;
|
border-bottom: 0px solid transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.greybackground.statustab{
|
|
||||||
border-top-right-radius: 0em !important;
|
|
||||||
padding: 2em 2em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standardContainer{
|
.standardContainer{
|
||||||
padding-left: 1.2em;
|
padding-left: 1.2em;
|
||||||
padding-right: 1.2em;
|
padding-right: 1.2em;
|
||||||
@ -301,8 +304,12 @@ body{
|
|||||||
color: #5e5d5d;
|
color: #5e5d5d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.segment{
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ui.secondary.vertical.menu .active.item{
|
.ui.secondary.vertical.menu .active.item{
|
||||||
background-color: #414141;
|
background: var(--theme_background);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
@ -311,11 +318,20 @@ body{
|
|||||||
animation: blinker 3s ease-in-out infinite;
|
animation: blinker 3s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.important.basic.segment{
|
||||||
|
background: linear-gradient(217deg, rgba(234,238,175,1) 16%, rgba(254,255,242,1) 78%);
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic.segment.advanceoptions{
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.bluefont{
|
.bluefont{
|
||||||
color: #417ac1 !important;
|
color: #417ac1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes blinker {
|
@keyframes blinker {
|
||||||
50% {
|
50% {
|
||||||
opacity: 50%;
|
opacity: 50%;
|
||||||
@ -327,52 +343,26 @@ body{
|
|||||||
*/
|
*/
|
||||||
#serverstatus{
|
#serverstatus{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border-radius: 1em;
|
||||||
|
margin: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#statusTitle{
|
#statusTitle{
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statustab{
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greybackground.statustab{
|
|
||||||
background-color: #414141 !important;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.greybackground.statustab .ui.header:not(:first-child){
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greybackground.statustab span,
|
|
||||||
.greybackground.statustab h1,
|
|
||||||
.greybackground.statustab h2,
|
|
||||||
.greybackground.statustab h3,
|
|
||||||
.greybackground.statustab h4,
|
|
||||||
.greybackground.statustab h5 {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.greybackground.statustab .header{
|
|
||||||
color: #b7b7b7 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#serverstatus.green{
|
#serverstatus.green{
|
||||||
background-color: #fefefe !important;
|
background: linear-gradient(60deg, #27e7ff, #00ca52);
|
||||||
border-right: 5px solid #3d9c64;
|
|
||||||
}
|
}
|
||||||
#serverstatus.green .sub.header{
|
#serverstatus.green .sub.header{
|
||||||
color: rgb(224, 224, 224);
|
color: rgb(224, 224, 224);
|
||||||
}
|
}
|
||||||
#serverstatus.green i,
|
#serverstatus.green i,
|
||||||
#serverstatus.green #statusTitle{
|
#serverstatus.green #statusTitle{
|
||||||
color: #3d9c64;
|
color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
#serverstatus.green #statusText{
|
#serverstatus.green #statusText{
|
||||||
color: #2c583d;
|
color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -381,22 +371,20 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#serverstatus:not(.green){
|
#serverstatus:not(.green){
|
||||||
background-color: white !important;
|
background: linear-gradient(215deg, rgba(38,60,71,1) 13%, rgba(2,3,42,1) 84%);
|
||||||
background-image: url("img/plant.png");
|
|
||||||
background-position: right;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: auto 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#serverstatus:not(.green) #statusTitle,
|
#serverstatus:not(.green) #statusTitle,
|
||||||
#serverstatus:not(.green) i,
|
#serverstatus:not(.green) i,
|
||||||
#serverstatus:not(.green) .sub.header{
|
#serverstatus:not(.green) .sub.header{
|
||||||
color: #4c4c4c;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.statustab{
|
.statustab{
|
||||||
min-height: 5.5em;
|
min-height: 5.5em;
|
||||||
|
margin: 1em;
|
||||||
|
border-radius: 1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#summaryTotalCount{
|
#summaryTotalCount{
|
||||||
@ -467,7 +455,7 @@ body{
|
|||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-color: #3d9c64;
|
background-color: white;
|
||||||
transform: scale(1.5);
|
transform: scale(1.5);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
<link rel="icon" type="image/png" href="./favicon.png" />
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||||
<title>Account Reset | Zoraxy</title>
|
<title>Account Reset | Zoraxy</title>
|
||||||
<link rel="stylesheet" href="script/semantic/semantic.min.css">
|
<link rel="stylesheet" href="script/semantic/semantic.min.css">
|
||||||
|
<link href="script/aos.css" rel="stylesheet">
|
||||||
|
<script src="script/aos.js"></script>
|
||||||
<script type="application/javascript" src="script/jquery-3.6.0.min.js"></script>
|
<script type="application/javascript" src="script/jquery-3.6.0.min.js"></script>
|
||||||
<script type="application/javascript" src="script/semantic/semantic.min.js"></script>
|
<script type="application/javascript" src="script/semantic/semantic.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: rgb(245,245,245);
|
background: rgb(38,60,71);
|
||||||
background: linear-gradient(28deg, rgba(245,245,245,1) 63%, rgba(255,255,255,1) 100%);
|
background: linear-gradient(215deg, rgba(38,60,71,1) 13%, rgba(2,3,42,1) 84%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.background{
|
.background{
|
||||||
@ -34,23 +36,6 @@
|
|||||||
margin:auto;
|
margin:auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loginForm{
|
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
|
||||||
width: 25em;
|
|
||||||
margin-left: 10em;
|
|
||||||
margin-top: 0 !important;
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 550px) {
|
|
||||||
/* CSS rules here for screens lower than 750px */
|
|
||||||
#loginForm{
|
|
||||||
width: calc(100% - 4em);
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#errmsg{
|
#errmsg{
|
||||||
color: #9f3a38;
|
color: #9f3a38;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
@ -61,18 +46,102 @@
|
|||||||
.backBtn{
|
.backBtn{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0em;
|
top: 0em;
|
||||||
left: 1em;
|
left: 2em;
|
||||||
margin-top: -4em;
|
transition: opacity 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backBtn:hover{
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginForm {
|
||||||
|
border-radius: 1em;
|
||||||
|
width: 25em;
|
||||||
|
height: 550px;
|
||||||
|
position: absolute; /*Can also be `fixed`*/
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
/*Solves a problem in which the content is being cut when the div is smaller than its' wrapper:*/
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wavebase {
|
||||||
|
position:fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height:5vh;
|
||||||
|
text-align:center;
|
||||||
|
padding-top: 1em;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Waves CSS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#wavesWrapper{
|
||||||
|
position: fixed;
|
||||||
|
bottom: 5vh;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waves {
|
||||||
|
position:relative;
|
||||||
|
width: 100%;
|
||||||
|
height:15vh;
|
||||||
|
margin-bottom:-7px; /*Fix for safari gap*/
|
||||||
|
min-height:100px;
|
||||||
|
max-height:150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.parallax > use {
|
||||||
|
animation: move-forever 25s cubic-bezier(.55,.5,.45,.5) infinite;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(1) {
|
||||||
|
animation-delay: -8s;
|
||||||
|
animation-duration: 28s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(2) {
|
||||||
|
animation-delay: -12s;
|
||||||
|
animation-duration: 40s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(3) {
|
||||||
|
animation-delay: -16s;
|
||||||
|
animation-duration: 52s;
|
||||||
|
}
|
||||||
|
.parallax > use:nth-child(4) {
|
||||||
|
animation-delay: -20s;
|
||||||
|
animation-duration: 80s;
|
||||||
|
}
|
||||||
|
@keyframes move-forever {
|
||||||
|
0% {
|
||||||
|
transform: translate3d(-90px,0,0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate3d(85px,0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*Shrinking for mobile*/
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.waves {
|
||||||
|
height:40px;
|
||||||
|
min-height:40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="background"></div>
|
<div id="loginForm" class="ui middle aligned center aligned grid" data-aos="fade-up">
|
||||||
<div id="loginForm" class="ui middle aligned center aligned grid">
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<a class="backBtn" href="/">
|
|
||||||
<i class="huge black chevron circle left icon"></i>
|
|
||||||
</a>
|
|
||||||
<form class="ui large form">
|
<form class="ui large form">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<img class="ui fluid image" src="img/public/logo.svg" style="pointer-events:none;">
|
<img class="ui fluid image" src="img/public/logo.svg" style="pointer-events:none;">
|
||||||
@ -95,7 +164,7 @@
|
|||||||
<input id="magic" type="password" name="New password" placeholder="New Password">
|
<input id="magic" type="password" name="New password" placeholder="New Password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="resetBtn" class="ui fluid basic green button">Set New Password</div>
|
<div id="resetBtn" class="ui fluid basic button"><i class="ui green lock open icon"></i> Reset Password</div>
|
||||||
<div id="errmsg" class="ui red message" style="display: none;">
|
<div id="errmsg" class="ui red message" style="display: none;">
|
||||||
<i class="red remove icon"></i> Unknown Error Occured
|
<i class="red remove icon"></i> Unknown Error Occured
|
||||||
</div>
|
</div>
|
||||||
@ -107,12 +176,36 @@
|
|||||||
<a href="#" id="resendEmailLink" onclick="sendResetAccountEmail();">Resend Email</a>
|
<a href="#" id="resendEmailLink" onclick="sendResetAccountEmail();">Resend Email</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
|
||||||
<small>Proudly powered by Zoraxy</small>
|
|
||||||
</form>
|
</form>
|
||||||
|
<a class="backBtn" href="/">
|
||||||
|
<i class="big chevron circle left icon" style="color: #121d37;"></i>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="wavesWrapper">
|
||||||
|
<!-- CSS waves-->
|
||||||
|
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
||||||
|
<defs>
|
||||||
|
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
||||||
|
</defs>
|
||||||
|
<g class="parallax">
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(255,255,255,0.7" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(255,255,255,0.5)" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(255,255,255,0.3)" />
|
||||||
|
<use xlink:href="#gentle-wave" x="48" y="7" fill="#fff" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="wavebase">
|
||||||
|
<p>Proudly powered by <a href="https://zoraxy.arozos.com" target="_blank">Zoraxy</a></p>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
AOS.init();
|
||||||
|
|
||||||
|
|
||||||
var redirectionAddress = "/";
|
var redirectionAddress = "/";
|
||||||
var loginAddress = "/api/auth/login";
|
var loginAddress = "/api/auth/login";
|
||||||
$(".checkbox").checkbox();
|
$(".checkbox").checkbox();
|
||||||
@ -155,6 +248,12 @@
|
|||||||
var token = $('#token').val();
|
var token = $('#token').val();
|
||||||
var newPassword = $('#magic').val();
|
var newPassword = $('#magic').val();
|
||||||
|
|
||||||
|
if (token.trim() == ""){
|
||||||
|
$("#errmsg").html(`<i class="red circle times icon"></i> Token cannot be empty!`);
|
||||||
|
$("#errmsg").show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Send POST request with input values as data
|
// Send POST request with input values as data
|
||||||
$.post('/api/account/new', { username: username, token: token, newpw: newPassword })
|
$.post('/api/account/new', { username: username, token: token, newpw: newPassword })
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
|
1
src/web/script/aos.css
Normal file
1
src/web/script/aos.css
Normal file
File diff suppressed because one or more lines are too long
1
src/web/script/aos.js
Normal file
1
src/web/script/aos.js
Normal file
File diff suppressed because one or more lines are too long
@ -41,8 +41,7 @@
|
|||||||
<input id="inlineEditBasicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
|
<input id="inlineEditBasicAuthCredPassword" type="password" placeholder="Password" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="field" >
|
<div class="field" >
|
||||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
|
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="green add icon"></i> Add Credential</button>
|
||||||
<button class="ui basic button" style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save Credential</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -69,7 +68,7 @@
|
|||||||
<small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
|
<small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field" >
|
<div class="field" >
|
||||||
<button class="ui basic button" onclick="addExceptionPath();"><i class="blue add icon"></i> Add Exception</button>
|
<button class="ui basic button" onclick="addExceptionPath();"><i class="yellow add icon"></i> Add Exception</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui basic message">
|
<div class="ui basic message">
|
||||||
@ -99,7 +98,7 @@
|
|||||||
let payloadHash = window.location.hash.substr(1);
|
let payloadHash = window.location.hash.substr(1);
|
||||||
try{
|
try{
|
||||||
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
|
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
|
||||||
loadBasicAuthCredentials(payloadHash.ept, payloadHash.ep);
|
loadBasicAuthCredentials(payloadHash.ep);
|
||||||
$("#epname").text(payloadHash.ep);
|
$("#epname").text(payloadHash.ep);
|
||||||
editingEndpoint = payloadHash;
|
editingEndpoint = payloadHash;
|
||||||
}catch(ex){
|
}catch(ex){
|
||||||
@ -107,13 +106,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadBasicAuthCredentials(endpointType, uuid){
|
function loadBasicAuthCredentials(uuid){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/updateCredentials",
|
url: "/api/proxy/updateCredentials",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
data: {
|
data: {
|
||||||
ep: uuid,
|
ep: uuid,
|
||||||
ptype: endpointType
|
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
//Push the existing account to list
|
//Push the existing account to list
|
||||||
@ -163,6 +161,9 @@
|
|||||||
|
|
||||||
// Update the table body with the credentials
|
// Update the table body with the credentials
|
||||||
updateEditingCredentialList();
|
updateEditingCredentialList();
|
||||||
|
|
||||||
|
//Save the table
|
||||||
|
saveCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addExceptionPath(){
|
function addExceptionPath(){
|
||||||
@ -175,7 +176,6 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/auth/exceptions/add",
|
url: "/api/proxy/auth/exceptions/add",
|
||||||
data:{
|
data:{
|
||||||
ptype: editingEndpoint.ept,
|
|
||||||
ep: editingEndpoint.ep,
|
ep: editingEndpoint.ep,
|
||||||
prefix: newExclusionPathMatchingPrefix
|
prefix: newExclusionPathMatchingPrefix
|
||||||
},
|
},
|
||||||
@ -197,7 +197,6 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/auth/exceptions/delete",
|
url: "/api/proxy/auth/exceptions/delete",
|
||||||
data:{
|
data:{
|
||||||
ptype: editingEndpoint.ept,
|
|
||||||
ep: editingEndpoint.ep,
|
ep: editingEndpoint.ep,
|
||||||
prefix: matchingPrefix
|
prefix: matchingPrefix
|
||||||
},
|
},
|
||||||
@ -271,6 +270,8 @@
|
|||||||
|
|
||||||
// Update the table body
|
// Update the table body
|
||||||
updateEditingCredentialList();
|
updateEditingCredentialList();
|
||||||
|
|
||||||
|
saveCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
function alreadyExists(username){
|
function alreadyExists(username){
|
||||||
@ -293,7 +294,6 @@
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
data: {
|
data: {
|
||||||
ep: editingEndpoint.ep,
|
ep: editingEndpoint.ep,
|
||||||
ptype: editingEndpoint.ept,
|
|
||||||
creds: JSON.stringify(editingCredentials)
|
creds: JSON.stringify(editingCredentials)
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
|
@ -119,11 +119,10 @@ func UpdateUptimeMonitorTargets() {
|
|||||||
|
|
||||||
// Generate uptime monitor targets from reverse proxy rules
|
// Generate uptime monitor targets from reverse proxy rules
|
||||||
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
||||||
subds := dp.GetSDProxyEndpointsAsMap()
|
hosts := dp.GetProxyEndpointsAsMap()
|
||||||
vdirs := dp.GetVDProxyEndpointsAsMap()
|
|
||||||
|
|
||||||
UptimeTargets := []*uptime.Target{}
|
UptimeTargets := []*uptime.Target{}
|
||||||
for subd, target := range subds {
|
for subd, target := range hosts {
|
||||||
url := "http://" + target.Domain
|
url := "http://" + target.Domain
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
if target.RequireTLS {
|
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
|
return UptimeTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user