mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +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/requestIsProxied", HandleManagementProxyCheck)
|
||||
//Reverse proxy root related APIs
|
||||
authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
||||
authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
|
||||
//authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
||||
//authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
|
||||
//Reverse proxy auth related APIs
|
||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
||||
|
173
src/config.go
173
src/config.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -35,97 +36,119 @@ type Record struct {
|
||||
BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
// Save a reverse proxy config record to file
|
||||
func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error {
|
||||
//TODO: Make this accept new def types
|
||||
os.MkdirAll("./conf/proxy/", 0775)
|
||||
filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
|
||||
|
||||
//Generate record
|
||||
thisRecord := proxyConfigRecord
|
||||
|
||||
//Write to file
|
||||
js, _ := json.MarshalIndent(thisRecord, "", " ")
|
||||
return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
||||
}
|
||||
|
||||
// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion)
|
||||
func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error {
|
||||
recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint)
|
||||
/*
|
||||
Load Reverse Proxy Config from file and append it to current runtime proxy router
|
||||
*/
|
||||
func LoadReverseProxyConfig(configFilepath string) error {
|
||||
//Load the config file from disk
|
||||
endpointConfig, err := os.ReadFile(configFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return SaveReverseProxyConfigToFile(recordToSave)
|
||||
}
|
||||
|
||||
func RemoveReverseProxyConfigFile(rootname string) error {
|
||||
filename := getFilenameFromRootName(rootname)
|
||||
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
||||
SystemWideLogger.Println("Config Removed: ", removePendingFile)
|
||||
if utils.FileExists(removePendingFile) {
|
||||
err := os.Remove(removePendingFile)
|
||||
if err != nil {
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err)
|
||||
return err
|
||||
}
|
||||
//Parse it into dynamic proxy endpoint
|
||||
thisConfigEndpoint := dynamicproxy.ProxyEndpoint{}
|
||||
err = json.Unmarshal(endpointConfig, &thisConfigEndpoint)
|
||||
if err != nil {
|
||||
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 ptype, rootname and proxyTarget, error if any
|
||||
func LoadReverseProxyConfig(filename string) (*Record, error) {
|
||||
thisRecord := Record{
|
||||
ProxyType: "",
|
||||
Rootname: "",
|
||||
ProxyTarget: "",
|
||||
UseTLS: false,
|
||||
func filterProxyConfigFilename(filename string) string {
|
||||
//Filter out wildcard characters
|
||||
filename = strings.ReplaceAll(filename, "*", "(ST)")
|
||||
filename = strings.ReplaceAll(filename, "?", "(QM)")
|
||||
filename = strings.ReplaceAll(filename, "[", "(OB)")
|
||||
filename = strings.ReplaceAll(filename, "]", "(CB)")
|
||||
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,
|
||||
SkipTlsValidation: false,
|
||||
SkipCertValidations: false,
|
||||
VirtualDirectories: []*dynamicproxy.ProxyEndpoint{},
|
||||
RequireBasicAuth: false,
|
||||
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
||||
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(filename)
|
||||
DefaultSiteOption: dynamicproxy.DefaultSite_InternalStaticWebServer,
|
||||
DefaultSiteValue: "",
|
||||
})
|
||||
if err != nil {
|
||||
return &thisRecord, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Unmarshal the content into config
|
||||
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
|
||||
return rootProxyEndpoint, nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -49,9 +49,9 @@ var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
||||
|
||||
var (
|
||||
name = "Zoraxy"
|
||||
version = "2.6.8"
|
||||
version = "3.0.0"
|
||||
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()
|
||||
|
||||
/*
|
||||
|
@ -3,7 +3,6 @@ package dynamicproxy
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -80,38 +79,26 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
/*
|
||||
Subdomain Routing
|
||||
Host Routing
|
||||
*/
|
||||
if strings.Contains(r.Host, ".") {
|
||||
//This might be a subdomain. See if there are any subdomain proxy router for this
|
||||
sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
|
||||
if sep != nil {
|
||||
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)
|
||||
sep := h.Parent.getProxyEndpointFromHostname(domainOnly)
|
||||
if sep != nil {
|
||||
if sep.RequireBasicAuth {
|
||||
err := h.handleBasicAuthRouting(w, r, sep)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
h.proxyRequest(w, r, targetProxyEndpoint)
|
||||
} else if !strings.HasSuffix(proxyingPath, "/") {
|
||||
h.hostRequest(w, r, sep)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Root Router Handling
|
||||
*/
|
||||
//Clean up the request URI
|
||||
proxyingPath := strings.TrimSpace(r.RequestURI)
|
||||
if !strings.HasSuffix(proxyingPath, "/") {
|
||||
potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
|
||||
if potentialProxtEndpoint != nil {
|
||||
//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.
|
||||
*/
|
||||
func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
domainOnly := r.Host
|
||||
if strings.Contains(r.Host, ":") {
|
||||
hostPath := strings.Split(r.Host, ":")
|
||||
domainOnly = hostPath[0]
|
||||
}
|
||||
|
||||
if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
|
||||
//Route to custom domain
|
||||
if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
|
||||
//Not set. Redirect to first level of domain redirectable
|
||||
fld, err := h.getTopLevelRedirectableDomain(domainOnly)
|
||||
if err != nil {
|
||||
//Redirect to proxy root
|
||||
h.proxyRequest(w, r, h.Parent.Root)
|
||||
} else {
|
||||
log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
|
||||
h.logRequest(r, false, 307, "root-redirect", domainOnly)
|
||||
http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
|
||||
}
|
||||
return
|
||||
} else if h.isTopLevelRedirectableDomain(domainOnly) {
|
||||
//This is requesting a top level private domain that should be serving root
|
||||
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)
|
||||
}
|
||||
//Get the proxy root config
|
||||
proot := h.Parent.Root
|
||||
switch proot.DefaultSiteOption {
|
||||
case DefaultSite_InternalStaticWebServer:
|
||||
fallthrough
|
||||
case DefaultSite_ReverseProxy:
|
||||
//They both share the same behavior
|
||||
h.vdirRequest(w, r, h.Parent.Root)
|
||||
case DefaultSite_Redirect:
|
||||
redirectTarget := strings.TrimSpace(proot.DefaultSiteValue)
|
||||
if redirectTarget == "" {
|
||||
redirectTarget = "about:blank"
|
||||
}
|
||||
} else {
|
||||
//Route to root
|
||||
h.proxyRequest(w, r, h.Parent.Root)
|
||||
|
||||
//Check if it is an infinite loopback redirect
|
||||
parsedURL, err := url.Parse(proot.DefaultSiteValue)
|
||||
if err != nil {
|
||||
//Error when parsing target. Send to root
|
||||
h.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()
|
||||
if !ok {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
@ -48,7 +44,7 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
if !matchingFound {
|
||||
h.logRequest(r, false, 401, proxyType, pe.Domain)
|
||||
h.logRequest(r, false, 401, "host", pe.Domain)
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
w.WriteHeader(401)
|
||||
return errors.New("unauthorized")
|
||||
|
@ -22,15 +22,13 @@ import (
|
||||
|
||||
func NewDynamicProxy(option RouterOption) (*Router, error) {
|
||||
proxyMap := sync.Map{}
|
||||
domainMap := sync.Map{}
|
||||
thisRouter := Router{
|
||||
Option: &option,
|
||||
ProxyEndpoints: &proxyMap,
|
||||
SubdomainEndpoint: &domainMap,
|
||||
Running: false,
|
||||
server: nil,
|
||||
routingRules: []*RoutingRule{},
|
||||
tldMap: map[string]int{},
|
||||
Option: &option,
|
||||
ProxyEndpoints: &proxyMap,
|
||||
Running: false,
|
||||
server: nil,
|
||||
routingRules: []*RoutingRule{},
|
||||
tldMap: map[string]int{},
|
||||
}
|
||||
|
||||
thisRouter.mux = &ProxyHandler{
|
||||
@ -84,13 +82,6 @@ func (router *Router) StartProxyService() error {
|
||||
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
|
||||
if router.Option.ForceTLSLatest {
|
||||
minVersion = tls.VersionTLS12
|
||||
@ -129,7 +120,7 @@ func (router *Router) StartProxyService() error {
|
||||
hostPath := strings.Split(r.Host, ":")
|
||||
domainOnly = hostPath[0]
|
||||
}
|
||||
sep := router.getSubdomainProxyEndpointFromHostname(domainOnly)
|
||||
sep := router.getProxyEndpointFromHostname(domainOnly)
|
||||
if sep != nil && sep.BypassGlobalTLS {
|
||||
//Allow routing via non-TLS handler
|
||||
originalHostHeader := r.Host
|
||||
@ -140,7 +131,7 @@ func (router *Router) StartProxyService() error {
|
||||
r.URL, _ = url.Parse(originalHostHeader)
|
||||
}
|
||||
|
||||
sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
sep.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
ProxyDomain: sep.Domain,
|
||||
OriginalHost: originalHostHeader,
|
||||
UseTLS: sep.RequireTLS,
|
||||
@ -280,128 +271,17 @@ func (router *Router) IsProxiedSubdomain(r *http.Request) bool {
|
||||
hostname = r.Host
|
||||
}
|
||||
hostname = strings.Split(hostname, ":")[0]
|
||||
subdEndpoint := router.getSubdomainProxyEndpointFromHostname(hostname)
|
||||
subdEndpoint := router.getProxyEndpointFromHostname(hostname)
|
||||
return subdEndpoint != nil
|
||||
}
|
||||
|
||||
/*
|
||||
Add an URL into a custom proxy services
|
||||
*/
|
||||
func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) error {
|
||||
domain := options.Domain
|
||||
if domain[len(domain)-1:] == "/" {
|
||||
domain = domain[:len(domain)-1]
|
||||
}
|
||||
|
||||
/*
|
||||
if rootname[len(rootname)-1:] == "/" {
|
||||
rootname = rootname[:len(rootname)-1]
|
||||
}
|
||||
*/
|
||||
|
||||
webProxyEndpoint := domain
|
||||
if options.RequireTLS {
|
||||
webProxyEndpoint = "https://" + webProxyEndpoint
|
||||
} else {
|
||||
webProxyEndpoint = "http://" + webProxyEndpoint
|
||||
}
|
||||
//Create a new proxy agent for this root
|
||||
path, err := url.Parse(webProxyEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations)
|
||||
|
||||
endpointObject := ProxyEndpoint{
|
||||
ProxyType: ProxyType_Vdir,
|
||||
RootOrMatchingDomain: options.RootName,
|
||||
Domain: domain,
|
||||
RequireTLS: options.RequireTLS,
|
||||
SkipCertValidations: options.SkipCertValidations,
|
||||
RequireBasicAuth: options.RequireBasicAuth,
|
||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||
Proxy: proxy,
|
||||
}
|
||||
|
||||
router.ProxyEndpoints.Store(options.RootName, &endpointObject)
|
||||
|
||||
log.Println("Registered Proxy Rule: ", options.RootName+" to "+domain)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Load routing from RP
|
||||
*/
|
||||
func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error) {
|
||||
if ptype == "vdir" {
|
||||
proxy, ok := router.ProxyEndpoints.Load(key)
|
||||
if !ok {
|
||||
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)
|
||||
func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) {
|
||||
var targetProxyEndpoint *ProxyEndpoint
|
||||
router.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||
key, ok := key.(string)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
@ -409,13 +289,32 @@ func (r *Router) GetSDProxyEndpointsAsMap() map[string]*ProxyEndpoint {
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
m[k] = v
|
||||
|
||||
if key == matchingDomain {
|
||||
targetProxyEndpoint = v
|
||||
}
|
||||
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)
|
||||
r.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||
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
|
||||
|
||||
import "errors"
|
||||
|
||||
/*
|
||||
ProxyEndpoint.go
|
||||
author: tobychui
|
||||
@ -12,54 +10,20 @@ import "errors"
|
||||
Most of the functions are implemented in dynamicproxy.go
|
||||
*/
|
||||
|
||||
//Get the string version of proxy type
|
||||
func (ep *ProxyEndpoint) GetProxyTypeString() string {
|
||||
if ep.ProxyType == ProxyType_Subdomain {
|
||||
return "subd"
|
||||
} else if ep.ProxyType == ProxyType_Vdir {
|
||||
return "vdir"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
//Update change in the current running proxy endpoint config
|
||||
// Update change in the current running proxy endpoint config
|
||||
func (ep *ProxyEndpoint) UpdateToRuntime() {
|
||||
if ep.IsVdir() {
|
||||
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
||||
|
||||
} else if ep.IsSubDomain() {
|
||||
ep.parent.SubdomainEndpoint.Store(ep.RootOrMatchingDomain, ep)
|
||||
}
|
||||
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
||||
}
|
||||
|
||||
//Return true if the endpoint type is virtual directory
|
||||
func (ep *ProxyEndpoint) IsVdir() bool {
|
||||
return ep.ProxyType == ProxyType_Vdir
|
||||
}
|
||||
|
||||
//Return true if the endpoint type is subdomain
|
||||
func (ep *ProxyEndpoint) IsSubDomain() bool {
|
||||
return ep.ProxyType == ProxyType_Subdomain
|
||||
}
|
||||
|
||||
//Remove this proxy endpoint from running proxy endpoint list
|
||||
// Remove this proxy endpoint from running proxy endpoint list
|
||||
func (ep *ProxyEndpoint) Remove() error {
|
||||
//fmt.Println(ptype, key)
|
||||
if ep.IsVdir() {
|
||||
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
||||
return nil
|
||||
} else if ep.IsSubDomain() {
|
||||
ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain)
|
||||
return nil
|
||||
}
|
||||
return errors.New("invalid or unsupported type")
|
||||
|
||||
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
||||
return nil
|
||||
}
|
||||
|
||||
//ProxyEndpoint remove provide global access by key
|
||||
func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
|
||||
targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain)
|
||||
// ProxyEndpoint remove provide global access by key
|
||||
func (router *Router) RemoveProxyEndpointByRootname(rootnameOrMatchingDomain string) error {
|
||||
targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
||||
@ -28,13 +29,28 @@ func (router *Router) getTargetProxyEndpointFromRequestURI(requestURI string) *P
|
||||
return targetProxyEndpoint
|
||||
}
|
||||
|
||||
func (router *Router) getSubdomainProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
|
||||
func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoint {
|
||||
var targetSubdomainEndpoint *ProxyEndpoint = nil
|
||||
ep, ok := router.SubdomainEndpoint.Load(hostname)
|
||||
ep, ok := router.ProxyEndpoints.Load(hostname)
|
||||
if ok {
|
||||
targetSubdomainEndpoint = ep.(*ProxyEndpoint)
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
@ -54,8 +70,8 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
||||
return rewrittenURL
|
||||
}
|
||||
|
||||
// Handle subdomain request
|
||||
func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
||||
// Handle host request
|
||||
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-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
||||
requestURL := r.URL.String()
|
||||
@ -89,7 +105,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request,
|
||||
r.URL, _ = url.Parse(originalHostHeader)
|
||||
}
|
||||
|
||||
err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
ProxyDomain: target.Domain,
|
||||
OriginalHost: originalHostHeader,
|
||||
UseTLS: target.RequireTLS,
|
||||
@ -113,7 +129,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.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)
|
||||
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)
|
||||
}
|
||||
|
||||
err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||
ProxyDomain: target.Domain,
|
||||
OriginalHost: originalHostHeader,
|
||||
UseTLS: target.RequireTLS,
|
||||
|
@ -14,8 +14,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ProxyType_Subdomain = 0
|
||||
ProxyType_Vdir = 1
|
||||
ProxyType_Root = 0
|
||||
ProxyType_Host = 1
|
||||
ProxyType_Vdir = 2
|
||||
)
|
||||
|
||||
type ProxyHandler struct {
|
||||
@ -37,16 +38,14 @@ type RouterOption struct {
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
Option *RouterOption
|
||||
ProxyEndpoints *sync.Map
|
||||
SubdomainEndpoint *sync.Map
|
||||
Running bool
|
||||
Root *ProxyEndpoint
|
||||
RootRoutingOptions *RootRoutingOptions
|
||||
mux http.Handler
|
||||
server *http.Server
|
||||
tlsListener net.Listener
|
||||
routingRules []*RoutingRule
|
||||
Option *RouterOption
|
||||
ProxyEndpoints *sync.Map
|
||||
Running bool
|
||||
Root *ProxyEndpoint
|
||||
mux http.Handler
|
||||
server *http.Server
|
||||
tlsListener net.Listener
|
||||
routingRules []*RoutingRule
|
||||
|
||||
tlsRedirectStop chan bool //Stop channel for tls redirection server
|
||||
tldMap map[string]int //Top level domain map, see tld.json
|
||||
@ -69,63 +68,48 @@ type BasicAuthExceptionRule struct {
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
// A proxy endpoint record
|
||||
// A proxy endpoint record, a general interface for handling inbound routing
|
||||
type ProxyEndpoint struct {
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key
|
||||
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:"-"`
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key
|
||||
Domain string //Domain or IP to proxy to
|
||||
|
||||
//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
|
||||
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
|
||||
type RootOptions struct {
|
||||
ProxyLocation string //Proxy Root target, all unset traffic will be forward to here
|
||||
RequireTLS bool //Proxy root target require TLS connection (not recommended)
|
||||
BypassGlobalTLS bool //Bypass global TLS setting and make root http only (not recommended)
|
||||
SkipCertValidations bool //Skip cert validation, suitable for self-signed certs, CURRENTLY NOT USED
|
||||
|
||||
//Basic Auth Related
|
||||
RequireBasicAuth bool //Require basic auth, CURRENTLY NOT USED
|
||||
BasicAuthCredentials []*BasicAuthCredentials
|
||||
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
const (
|
||||
DefaultSite_InternalStaticWebServer = 0
|
||||
DefaultSite_ReverseProxy = 1
|
||||
DefaultSite_Redirect = 2
|
||||
DefaultSite_NotFoundPage = 3
|
||||
)
|
||||
|
||||
/*
|
||||
Web Templates
|
||||
|
@ -89,7 +89,7 @@ func (ws *WebServer) RestorePreviousState() {
|
||||
ws.option.EnableDirectoryListing = enableDirList
|
||||
|
||||
//Check the running state
|
||||
webservRunning := false
|
||||
webservRunning := true
|
||||
ws.option.Sysdb.Read("webserv", "enabled", &webservRunning)
|
||||
if webservRunning {
|
||||
ws.Start()
|
||||
@ -124,6 +124,11 @@ func (ws *WebServer) ChangePort(port string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get current using port in options
|
||||
func (ws *WebServer) GetListeningPort() string {
|
||||
return ws.option.Port
|
||||
}
|
||||
|
||||
// Start starts the web server.
|
||||
func (ws *WebServer) Start() error {
|
||||
ws.mu.Lock()
|
||||
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@ -21,6 +22,9 @@ var (
|
||||
|
||||
// Add user customizable reverse proxy
|
||||
func ReverseProxtInit() {
|
||||
/*
|
||||
Load Reverse Proxy Global Settings
|
||||
*/
|
||||
inboundPort := 80
|
||||
if sysdb.KeyExists("settings", "inbound") {
|
||||
sysdb.Read("settings", "inbound", &inboundPort)
|
||||
@ -63,6 +67,12 @@ func ReverseProxtInit() {
|
||||
SystemWideLogger.Println("Force HTTPS mode disabled")
|
||||
}
|
||||
|
||||
/*
|
||||
Create a new proxy object
|
||||
The DynamicProxy is the parent of all reverse proxy handlers,
|
||||
use for managemening and provide functions to access proxy handlers
|
||||
*/
|
||||
|
||||
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
||||
HostUUID: nodeUUID,
|
||||
Port: inboundPort,
|
||||
@ -83,45 +93,28 @@ func ReverseProxtInit() {
|
||||
|
||||
dynamicProxyRouter = dprouter
|
||||
|
||||
//Load all conf from files
|
||||
/*
|
||||
|
||||
Load all conf from files
|
||||
|
||||
*/
|
||||
confs, _ := filepath.Glob("./conf/proxy/*.config")
|
||||
for _, conf := range confs {
|
||||
record, err := LoadReverseProxyConfig(conf)
|
||||
err := LoadReverseProxyConfig(conf)
|
||||
if err != nil {
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if record.ProxyType == "root" {
|
||||
dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
|
||||
ProxyLocation: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
})
|
||||
} else if record.ProxyType == "subd" {
|
||||
dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
|
||||
MatchingDomain: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||
})
|
||||
} else if record.ProxyType == "vdir" {
|
||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
|
||||
RootName: record.Rootname,
|
||||
Domain: record.ProxyTarget,
|
||||
RequireTLS: record.UseTLS,
|
||||
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||
SkipCertValidations: record.SkipTlsValidation,
|
||||
RequireBasicAuth: record.RequireBasicAuth,
|
||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||
})
|
||||
} else {
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil)
|
||||
if dynamicProxyRouter.Root == nil {
|
||||
//Root config not set (new deployment?), use internal static web server as root
|
||||
defaultRootRouter, err := GetDefaultRootConfig()
|
||||
if err != nil {
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err)
|
||||
return
|
||||
}
|
||||
dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
|
||||
}
|
||||
|
||||
//Start Service
|
||||
@ -173,7 +166,7 @@ func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
|
||||
eptype, err := utils.PostPara(r, "type") //Support root and host
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "type not defined")
|
||||
return
|
||||
@ -241,73 +234,96 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
rootname := ""
|
||||
if eptype == "vdir" {
|
||||
vdir, 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")
|
||||
var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
|
||||
if eptype == "host" {
|
||||
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "subdomain not defined")
|
||||
return
|
||||
}
|
||||
rootname = subdomain
|
||||
thisOption := dynamicproxy.SubdOptions{
|
||||
MatchingDomain: subdomain,
|
||||
thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
|
||||
//I/O
|
||||
ProxyType: dynamicproxy.ProxyType_Host,
|
||||
RootOrMatchingDomain: rootOrMatchingDomain,
|
||||
Domain: endpoint,
|
||||
//TLS
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
//VDir
|
||||
VirtualDirectories: []*dynamicproxy.ProxyEndpoint{},
|
||||
//Auth
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
||||
DefaultSiteOption: 0,
|
||||
DefaultSiteValue: "",
|
||||
}
|
||||
|
||||
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "unable to prepare proxy route to target endpoint: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
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,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
BypassGlobalTLS: false,
|
||||
SkipCertValidations: false,
|
||||
|
||||
DefaultSiteOption: defaultSiteOption,
|
||||
DefaultSiteValue: dsVal,
|
||||
}
|
||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
||||
} else if eptype == "root" {
|
||||
rootname = "root"
|
||||
thisOption := dynamicproxy.RootOptions{
|
||||
ProxyLocation: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
|
||||
return
|
||||
}
|
||||
dynamicProxyRouter.SetRootProxy(&thisOption)
|
||||
|
||||
dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
|
||||
proxyEndpointCreated = &rootRoutingEndpoint
|
||||
} else {
|
||||
//Invalid eptype
|
||||
utils.SendErrorResponse(w, "Invalid endpoint type")
|
||||
utils.SendErrorResponse(w, "invalid endpoint type")
|
||||
return
|
||||
}
|
||||
|
||||
//Save it
|
||||
thisProxyConfigRecord := Record{
|
||||
ProxyType: eptype,
|
||||
Rootname: rootname,
|
||||
ProxyTarget: endpoint,
|
||||
UseTLS: useTLS,
|
||||
BypassGlobalTLS: useBypassGlobalTLS,
|
||||
SkipTlsValidation: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: basicAuthCredentials,
|
||||
//Save the config to file
|
||||
err = SaveReverseProxyConfig(proxyEndpointCreated)
|
||||
if err != nil {
|
||||
SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
|
||||
return
|
||||
}
|
||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||
|
||||
//Update utm if exists
|
||||
if uptimeMonitor != nil {
|
||||
@ -320,17 +336,11 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
/*
|
||||
ReverseProxyHandleEditEndpoint handles proxy endpoint edit
|
||||
This endpoint do not handle
|
||||
basic auth credential update. The credential
|
||||
will be loaded from old config and reused
|
||||
(host only, for root use Default Site page to edit)
|
||||
This endpoint do not handle basic auth credential update.
|
||||
The credential will be loaded from old config and reused
|
||||
*/
|
||||
func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "type not defined")
|
||||
return
|
||||
}
|
||||
|
||||
rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Target proxy rule not defined")
|
||||
@ -371,50 +381,31 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
requireBasicAuth := (rba == "true")
|
||||
|
||||
//Load the previous basic auth credentials from current proxy rules
|
||||
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain)
|
||||
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
|
||||
return
|
||||
}
|
||||
|
||||
if eptype == "vdir" {
|
||||
thisOption := dynamicproxy.VdirOptions{
|
||||
RootName: targetProxyEntry.RootOrMatchingDomain,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: false,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
targetProxyEntry.Remove()
|
||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
||||
//Generate a new proxyEndpoint from the new config
|
||||
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
||||
newProxyEndpoint.Domain = endpoint
|
||||
newProxyEndpoint.RequireTLS = useTLS
|
||||
newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
|
||||
newProxyEndpoint.SkipCertValidations = skipTlsValidation
|
||||
newProxyEndpoint.RequireBasicAuth = requireBasicAuth
|
||||
|
||||
} else if eptype == "subd" {
|
||||
thisOption := dynamicproxy.SubdOptions{
|
||||
MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
|
||||
Domain: endpoint,
|
||||
RequireTLS: useTLS,
|
||||
BypassGlobalTLS: bypassGlobalTLS,
|
||||
SkipCertValidations: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
targetProxyEntry.Remove()
|
||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
||||
//Prepare to replace the current routing rule
|
||||
readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
targetProxyEntry.Remove()
|
||||
dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
|
||||
|
||||
//Save it to file
|
||||
thisProxyConfigRecord := Record{
|
||||
ProxyType: eptype,
|
||||
Rootname: targetProxyEntry.RootOrMatchingDomain,
|
||||
ProxyTarget: endpoint,
|
||||
UseTLS: useTLS,
|
||||
SkipTlsValidation: skipTlsValidation,
|
||||
RequireBasicAuth: requireBasicAuth,
|
||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||
}
|
||||
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||
SaveReverseProxyConfig(newProxyEndpoint)
|
||||
|
||||
//Update uptime monitor
|
||||
UpdateUptimeMonitorTargets()
|
||||
@ -429,21 +420,20 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
//Remove the config from runtime
|
||||
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
|
||||
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//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
|
||||
if uptimeMonitor != nil {
|
||||
@ -473,14 +463,8 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.GetPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
@ -502,17 +486,6 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
if ptype != "vdir" && ptype != "subd" {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
creds, err := utils.PostPara(r, "creds")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
@ -520,7 +493,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
@ -570,7 +543,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
targetProxy.BasicAuthCredentials = mergedCredentials
|
||||
|
||||
//Save it to file
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
SaveReverseProxyConfig(targetProxy)
|
||||
|
||||
//Replace runtime configuration
|
||||
targetProxy.UpdateToRuntime()
|
||||
@ -593,14 +566,8 @@ func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.GetPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
@ -624,12 +591,6 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||
@ -637,7 +598,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
//Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
@ -666,7 +627,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
//Save configs to runtime and file
|
||||
targetProxy.UpdateToRuntime()
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
SaveReverseProxyConfig(targetProxy)
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
@ -679,12 +640,6 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
ptype, err := utils.PostPara(r, "ptype")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||
return
|
||||
}
|
||||
|
||||
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||
@ -692,7 +647,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// Load the target proxy object from router
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||
targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
@ -717,7 +672,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// Save configs to runtime and file
|
||||
targetProxy.UpdateToRuntime()
|
||||
SaveReverseProxyEndpointToFile(targetProxy)
|
||||
SaveReverseProxyConfig(targetProxy)
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
@ -728,16 +683,28 @@ func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
||||
eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
|
||||
eptype, err := utils.PostPara(r, "type") //Support root and host
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "type not defined")
|
||||
return
|
||||
}
|
||||
|
||||
if eptype == "vdir" {
|
||||
if eptype == "host" {
|
||||
results := []*dynamicproxy.ProxyEndpoint{}
|
||||
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||
results = append(results, value.(*dynamicproxy.ProxyEndpoint))
|
||||
thisEndpoint := 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
|
||||
})
|
||||
|
||||
@ -745,19 +712,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
||||
return results[i].Domain < results[j].Domain
|
||||
})
|
||||
|
||||
js, _ := json.Marshal(results)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
} else if eptype == "subd" {
|
||||
results := []*dynamicproxy.ProxyEndpoint{}
|
||||
dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
|
||||
results = append(results, value.(*dynamicproxy.ProxyEndpoint))
|
||||
return true
|
||||
})
|
||||
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
|
||||
})
|
||||
|
||||
js, _ := json.Marshal(results)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
} else if eptype == "root" {
|
||||
@ -881,34 +835,3 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
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 {
|
||||
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
|
||||
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
||||
if err != nil {
|
||||
@ -220,21 +232,6 @@ func startupSequence() {
|
||||
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
|
||||
|
@ -1,92 +1,119 @@
|
||||
<div class="standardContainer">
|
||||
<div class="ui basic segment">
|
||||
<h2>Set Proxy Root</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>
|
||||
<h2>Default Site</h2>
|
||||
<p>Default routing options for inbound traffic (previously called Proxy Root)</p>
|
||||
<div class="ui form">
|
||||
<div class="field">
|
||||
<label>Proxy Root</label>
|
||||
<input type="text" id="proxyRoot" onchange="checkRootRequireTLS(this.value);">
|
||||
<small>E.g. localhost:8080</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" id="rootReqTLS">
|
||||
<label>Root require TLS connection <br><small>Check this if your proxy root URL starts with https://</small></label>
|
||||
<div class="grouped fields">
|
||||
<label>What to show when Zoraxy is hit with an unknown Host?</label>
|
||||
<div class="field">
|
||||
<div class="ui radio defaultsite checkbox">
|
||||
<input type="radio" name="defaultsiteOption" checked="checked" value="webserver">
|
||||
<label>Internal Static Web Server<br>
|
||||
<small>Check this if you prefer a more Apache / Nginx like experience</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 class="ui horizontal divider">OR</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;">
|
||||
</div>
|
||||
|
||||
<!-- 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 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">
|
||||
<label>Redirect target domain</label>
|
||||
<div class="ui input">
|
||||
<input id="unsetRedirectDomain" type="text" placeholder="http://example.com">
|
||||
<input id="redirectDomain" type="text" placeholder="http://example.com">
|
||||
</div>
|
||||
<small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)<br>
|
||||
Leave empty for redirecting to upper level domain (e.g. notfound.example.com <i class="right arrow icon"></i> example.com)</small>
|
||||
<small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)</small>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="ui basic button" onclick="updateRootOptions()"><i class="blue save icon" ></i> Save Root Options</button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<script>
|
||||
var currentDefaultSiteOption = 0; //For enum see typedef.go
|
||||
$("#advanceRootSettings").accordion();
|
||||
|
||||
function handleUseStaticWebServerAsRoot(){
|
||||
let useStaticWebServer = $("#useStaticWebServer")[0].checked;
|
||||
if (useStaticWebServer){
|
||||
//Handle toggle events of option radio boxes
|
||||
function updateAvaibleDefaultSiteOptions(){
|
||||
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();
|
||||
$("#proxyRoot").val(staticWebServerURL);
|
||||
$("#proxyRoot").parent().addClass("disabled");
|
||||
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||
$("#rootReqTLS").parent().addClass("disabled");
|
||||
|
||||
//Check if web server is enabled. If not, ask if the user want to enable it
|
||||
/*if (!$("#webserv_enable").parent().checkbox("is checked")){
|
||||
confirmBox("Enable static web server now?", function(choice){
|
||||
if (choice == true){
|
||||
$("#webserv_enable").parent().checkbox("set checked");
|
||||
}
|
||||
});
|
||||
}*/
|
||||
}else{
|
||||
currentDefaultSiteOption = 0;
|
||||
}else if (selectedDefaultSite == "proxy"){
|
||||
$("#defaultSiteProxyOptions").show();
|
||||
$("#rootReqTLS").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){
|
||||
@ -94,6 +121,22 @@
|
||||
if (data == null){
|
||||
|
||||
}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);
|
||||
checkRootRequireTLS(data.Domain);
|
||||
}
|
||||
@ -104,21 +147,9 @@
|
||||
});
|
||||
}
|
||||
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){
|
||||
let currentProxyRoot = $("#proxyRoot").val().trim();
|
||||
@ -131,47 +162,12 @@
|
||||
|
||||
}
|
||||
|
||||
function updateRootSettingStates(){
|
||||
$.get("/api/cert/tls", function(data){
|
||||
if (data == true){
|
||||
$("#disableRootTLS").parent().removeClass('disabled').attr("title", "");
|
||||
}else{
|
||||
$("#disableRootTLS").parent().addClass('disabled').attr("title", "TLS listener is not enabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Bind event to tab switch
|
||||
tabSwitchEventBind["setroot"] = function(){
|
||||
//On switch over to this page, update root info
|
||||
updateRootSettingStates();
|
||||
|
||||
}
|
||||
|
||||
//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
|
||||
function checkRootRequireTLS(targetDomain){
|
||||
//Trim off the http or https from the origin
|
||||
@ -193,14 +189,15 @@
|
||||
}else if (data == "http"){
|
||||
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Set the new proxy root option
|
||||
function setProxyRoot(){
|
||||
function setProxyRoot(btn=undefined){
|
||||
if (btn != undefined){
|
||||
$(btn).addClass("disabled");
|
||||
}
|
||||
var newpr = $("#proxyRoot").val();
|
||||
if (newpr.trim() == ""){
|
||||
$("#proxyRoot").parent().addClass('error');
|
||||
@ -211,10 +208,36 @@
|
||||
|
||||
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
|
||||
$.ajax({
|
||||
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){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false, 5000);
|
||||
@ -231,37 +254,20 @@
|
||||
|
||||
setTimeout(function(){
|
||||
//Update the checkbox
|
||||
updateWebServerLinkSettings();
|
||||
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>
|
@ -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="standardContainer">
|
||||
<div class="ui basic segment" style="margin-top: 1em;">
|
||||
<h2>New Proxy Rule</h2>
|
||||
<p>You can create a proxy endpoing by subdomain or virtual directories</p>
|
||||
<div class="ui form">
|
||||
<div class="field">
|
||||
<label>Proxy Type</label>
|
||||
<div class="ui selection dropdown">
|
||||
<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 class="ui basic segment" style="border-radius: 1em; padding: 1em !important;">
|
||||
<h2>New Proxy Rule</h2>
|
||||
<p>You can add more proxy rules to support more site via domain / subdomains</p>
|
||||
<div class="ui form">
|
||||
<div class="field">
|
||||
<label>Matching Keyword / Domain</label>
|
||||
<input type="text" id="rootname" placeholder="mydomain.com">
|
||||
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com</small>
|
||||
</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 class="six wide column">
|
||||
<div class="ui basic segment" style="height: 100%; background-color: var(--theme_grey); color: var(--theme_lgrey);">
|
||||
<br>
|
||||
<span style="font-size: 1.2em; font-weight: 300;">Subdomain</span><br>
|
||||
Example of subdomain matching keyword:<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 basic segment rulesInstructions">
|
||||
<span style="font-size: 1.2em; font-weight: 300;"><i class="ui yellow star icon"></i> Domain</span><br>
|
||||
Example of domain matching keyword:<br>
|
||||
<code>arozos.com</code> <br>Any acess requesting arozos.com will be proxy to the IP address below<br>
|
||||
<div class="ui divider"></div>
|
||||
<span style="font-size: 1.2em; font-weight: 300;">Virtual Directory</span><br>
|
||||
Example of virtual directory name: <br>
|
||||
<code>/s1/home/</code> <br>(Any access to {this_server}/s1/home/ will be proxy to the IP address below)<br>
|
||||
You can also ignore the tailing slash for wildcard like usage.<br>
|
||||
<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> Subdomain</span><br>
|
||||
Example of subdomain matching keyword:<br>
|
||||
<code>s1.arozos.com</code> <br>Any request starting with s1.arozos.com will be proxy to the IP address below<br>
|
||||
<div class="ui divider"></div>
|
||||
<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="item"><code>/s1/room-101</code></div>
|
||||
<div class="item"><code>/s1/room-102/</code></div>
|
||||
<div class="item"><code>/s1/room-103/map.txt</code></div>
|
||||
</div><br>
|
||||
|
||||
<div class="item"><code>www.arozos.com</code></div>
|
||||
<div class="item"><code>foo.bar.arozos.com</code></div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
@ -124,7 +119,6 @@
|
||||
|
||||
//New Proxy Endpoint
|
||||
function newProxyEndpoint(){
|
||||
var type = $("#ptype").val();
|
||||
var rootname = $("#rootname").val();
|
||||
var proxyDomain = $("#proxyDomain").val();
|
||||
var useTLS = $("#reqTls")[0].checked;
|
||||
@ -132,20 +126,6 @@
|
||||
var bypassGlobalTLS = $("#bypassGlobalTLS")[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() == ""){
|
||||
$("#rootname").parent().addClass("error");
|
||||
return
|
||||
@ -164,7 +144,7 @@
|
||||
$.ajax({
|
||||
url: "/api/proxy/add",
|
||||
data: {
|
||||
type: type,
|
||||
type: "host",
|
||||
rootname: rootname,
|
||||
tls: useTLS,
|
||||
ep: proxyDomain,
|
||||
@ -178,8 +158,6 @@
|
||||
msgbox(data.error, false, 5000);
|
||||
}else{
|
||||
//OK
|
||||
listVdirs();
|
||||
listSubd();
|
||||
|
||||
|
||||
//Clear old data
|
||||
@ -189,7 +167,7 @@
|
||||
updateTable();
|
||||
|
||||
//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){
|
||||
if (choice == true){
|
||||
//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
|
||||
function deleteEndpoint(ptype, epoint){
|
||||
if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
|
||||
if (confirm("Confirm remove proxy for :" + epoint + "?")){
|
||||
$.ajax({
|
||||
url: "/api/proxy/del",
|
||||
data: {ep: epoint, ptype: ptype},
|
||||
data: {ep: epoint, },
|
||||
success: function(){
|
||||
listVdirs();
|
||||
listSubd();
|
||||
listProxyEndpoints();
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -330,14 +299,6 @@
|
||||
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
|
||||
*/
|
||||
@ -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="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();
|
||||
column.empty().append(`${originalContent}
|
||||
<div class="ui divider"></div>
|
||||
@ -429,9 +390,8 @@
|
||||
$("#" + endpointType).find(".editBtn").addClass("disabled");
|
||||
}
|
||||
|
||||
function exitProxyInlineEdit(){
|
||||
listSubd();
|
||||
listVdirs();
|
||||
function exitProxyInlineEdit(endpointType){
|
||||
listProxyEndpoints();
|
||||
$("#" + endpointType).find(".editBtn").removeClass("disabled");
|
||||
}
|
||||
|
||||
@ -440,14 +400,8 @@
|
||||
if (row.length == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
var epttype = $(row).attr("class");
|
||||
if (epttype == "subdEntry"){
|
||||
epttype = "subd";
|
||||
}else if (epttype == "vdirEntry"){
|
||||
epttype = "vdir";
|
||||
}
|
||||
|
||||
|
||||
var epttype = "host";
|
||||
let newDomain = $(row).find(".Domain").val();
|
||||
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
||||
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
||||
@ -473,11 +427,7 @@
|
||||
msgbox(data.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Proxy endpoint updated");
|
||||
if (epttype == "subd"){
|
||||
listSubd();
|
||||
}else if (epttype == "vdir"){
|
||||
listVdirs();
|
||||
}
|
||||
listProxyEndpoints();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="six wide column statisticWrapper">
|
||||
<div class="ui greybackground statustab segment">
|
||||
<div class="ui statustab segment">
|
||||
<h5 class="ui header">
|
||||
<i class="exchange icon"></i>
|
||||
<div class="content">
|
||||
@ -29,7 +29,7 @@
|
||||
</div>
|
||||
</h5>
|
||||
<div class="ui divider"></div>
|
||||
<h5 class="ui header">
|
||||
<h5 class="ui header" style="margin-top: 0px;">
|
||||
<i class="arrows alternate horizontal icon"></i>
|
||||
<div class="content">
|
||||
<span id="forwardtype"></span>
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</h5>
|
||||
<div class="ui divider"></div>
|
||||
<h5 class="ui header">
|
||||
<h5 class="ui header" style="margin-top: 0px;">
|
||||
<i class="map marker alternate icon"></i>
|
||||
<div class="content">
|
||||
<span id="country"></span>
|
||||
@ -51,20 +51,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="networkActWrapper" class="standardContainer" style="position: relative; margin-top: 1em;">
|
||||
<div class="standardContainer" style="padding-bottom: 0 !important;">
|
||||
<!-- 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>
|
||||
</div>
|
||||
<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>
|
||||
<br>
|
||||
<div class="standardContainer">
|
||||
<h4>Basic Settings</h4>
|
||||
<div class="ui divider"></div>
|
||||
<h4>Global Settings</h4>
|
||||
<p>Inbound Port (Port to be proxied)</p>
|
||||
<div class="ui action fluid notloopbackOnly input">
|
||||
<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>
|
||||
<br>
|
||||
<div id="tls" class="ui toggle notloopbackOnly checkbox">
|
||||
@ -99,11 +106,7 @@
|
||||
</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;">
|
||||
<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>
|
||||
@ -114,7 +117,7 @@
|
||||
<div class="ui two column stackable grid">
|
||||
<div class="column">
|
||||
<p>Visitor Counts</p>
|
||||
<table class="ui unstackable inverted celled table">
|
||||
<table class="ui unstackable very basic celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Country ISO Code</th>
|
||||
@ -130,7 +133,7 @@
|
||||
</div>
|
||||
<div class="column">
|
||||
<p>Proxy Request Types</p>
|
||||
<table class="ui unstackable inverted celled table">
|
||||
<table class="ui unstackable very basic celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Proxy Type</th>
|
||||
@ -175,13 +178,13 @@
|
||||
}
|
||||
$("#serverstatus").addClass("green");
|
||||
$("#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);
|
||||
}else{
|
||||
$("#startbtn").removeClass("disabled");
|
||||
$("#stopbtn").addClass("disabled");
|
||||
$("#statusTitle").text("Offline");
|
||||
$("#rpStatusIcon").attr("class", "black circle times icon")
|
||||
$("#rpStatusIcon").attr("class", "yellow moon icon")
|
||||
$("#statusText").text("Reverse proxy server is offline");
|
||||
$("#serverstatus").removeClass("green");
|
||||
}
|
||||
@ -565,6 +568,7 @@
|
||||
options: {
|
||||
animation: false,
|
||||
maintainAspectRatio: false,
|
||||
bezierCurve: true,
|
||||
tooltips: {enabled: false},
|
||||
hover: {mode: null},
|
||||
//stepped: 'middle',
|
||||
@ -606,18 +610,18 @@
|
||||
{
|
||||
label: 'Inbound',
|
||||
data: rxValues,
|
||||
borderColor: "#4d9dd9",
|
||||
borderWidth: 2,
|
||||
backgroundColor: 'rgba(77, 157, 217, 0.2)',
|
||||
borderColor: "#484bb8",
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(72, 75, 184, 0.2)',
|
||||
fill: true,
|
||||
pointStyle: false,
|
||||
},
|
||||
{
|
||||
label: 'Outbound',
|
||||
data: txValues,
|
||||
borderColor: '#ffe32b',
|
||||
borderWidth: 2,
|
||||
backgroundColor: 'rgba(255, 227, 43, 0.2)',
|
||||
borderColor: '#02a9c1',
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(2, 169, 193, 0.2)',
|
||||
fill: true,
|
||||
pointStyle: false,
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
<div class="standardContainer">
|
||||
<div class="ui basic segment">
|
||||
<h2>Subdomain</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>
|
||||
<h2>HTTP Proxy</h2>
|
||||
<p>Proxy HTTP server with HTTP or HTTPS. Sometime subdomain contain a prefix to the main domain name, separated by a dot. <br>For example, in the domain "blog.example.com," "blog" is the subdomain.</p>
|
||||
</div>
|
||||
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
||||
<table class="ui celled sortable unstackable compact table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Matching Domain</th>
|
||||
<th>Proxy To</th>
|
||||
<th>Host</th>
|
||||
<th>Destination</th>
|
||||
<th>Virtual Directory</th>
|
||||
<th>Basic Auth</th>
|
||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||
</tr>
|
||||
@ -18,13 +19,13 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function listSubd(){
|
||||
$.get("/api/proxy/list?type=subd", function(data){
|
||||
function listProxyEndpoints(){
|
||||
$.get("/api/proxy/list?type=host", function(data){
|
||||
$("#subdList").html(``);
|
||||
if (data.error !== undefined){
|
||||
$("#subdList").append(`<tr>
|
||||
@ -32,7 +33,7 @@
|
||||
</tr>`);
|
||||
}else if (data.length == 0){
|
||||
$("#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>`);
|
||||
}else{
|
||||
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">
|
||||
<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="vdir">WIP</td>
|
||||
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||
@ -61,6 +74,6 @@
|
||||
|
||||
//Bind on tab switch events
|
||||
tabSwitchEventBind["subd"] = function(){
|
||||
listSubd();
|
||||
listProxyEndpoints();
|
||||
}
|
||||
</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">
|
||||
<i class="simplistic info circle icon"></i>Status
|
||||
</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">
|
||||
<i class="simplistic folder icon"></i> Virtual Directory
|
||||
</a>
|
||||
<a class="item" tag="subd">
|
||||
<i class="simplistic sitemap icon"></i> Subdomain Proxy
|
||||
</a>
|
||||
<a class="item" tag="rules">
|
||||
<i class="simplistic plus square icon"></i> Create Proxy Rules
|
||||
</a>
|
||||
<a class="item" tag="setroot">
|
||||
<i class="simplistic home icon"></i> Set Proxy Root
|
||||
<a class="item" tag="tcpprox">
|
||||
<i class="simplistic exchange icon"></i> TCP Proxy
|
||||
</a>
|
||||
|
||||
|
||||
<div class="ui divider menudivider">Access & Connections</div>
|
||||
<a class="item" tag="cert">
|
||||
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||
@ -65,16 +71,14 @@
|
||||
<a class="item" tag="zgrok">
|
||||
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
||||
</a>
|
||||
<a class="item" tag="tcpprox">
|
||||
<i class="simplistic exchange icon"></i> TCP Proxy
|
||||
</a>
|
||||
<div class="ui divider menudivider">Others</div>
|
||||
|
||||
<div class="ui divider menudivider">Hosting</div>
|
||||
<a class="item" tag="webserv">
|
||||
<i class="simplistic globe icon"></i> Static Web Server
|
||||
</a>
|
||||
<a class="item" tag="utm">
|
||||
<i class="simplistic time icon"></i> Uptime Monitor
|
||||
</a>
|
||||
|
||||
<div class="ui divider menudivider">Others</div>
|
||||
|
||||
<a class="item" tag="networktool">
|
||||
<i class="simplistic terminal icon"></i> Network Tools
|
||||
</a>
|
||||
@ -235,7 +239,7 @@
|
||||
$.get("/api/auth/logout", function(response) {
|
||||
if (response === "OK") {
|
||||
setTimeout(function(){
|
||||
window.location.href = "/";
|
||||
window.location.href = "/login.html";
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
@ -7,48 +7,13 @@
|
||||
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||
<title>Login | Zoraxy</title>
|
||||
<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/semantic/semantic.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background: rgb(245,245,245);
|
||||
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;
|
||||
}
|
||||
background: linear-gradient(60deg, rgba(84,58,183,1) 0%, rgba(0,172,193,1) 100%);
|
||||
}
|
||||
|
||||
#errmsg{
|
||||
@ -65,13 +30,97 @@
|
||||
.ui.fluid.button.registerOnly{
|
||||
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>
|
||||
</head>
|
||||
<body>
|
||||
<div class="background"></div>
|
||||
<div id="loginForm" class="ui middle aligned center aligned grid">
|
||||
<div class="column">
|
||||
<form class="ui large form">
|
||||
<div id="loginForm" class="ui middle aligned center aligned grid" data-aos="fade-up">
|
||||
<div class="column" style="padding-top: 0 !important;">
|
||||
<form class="ui large form content">
|
||||
<div class="ui basic segment">
|
||||
<img class="ui fluid image" src="img/public/logo.svg" style="pointer-events:none;">
|
||||
<p class="registerOnly">Account Setup</p>
|
||||
@ -99,19 +148,37 @@
|
||||
<label>Remember Me</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loginbtn" class="ui fluid basic blue button loginOnly">Login</div>
|
||||
<div id="regsiterbtn" class="ui fluid basic blue button registerOnly">Create</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 button registerOnly"><i class="ui green checkmark icon"></i> Confirm</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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<small>Proudly powered by Zoraxy</small>
|
||||
</form>
|
||||
</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>
|
||||
AOS.init();
|
||||
|
||||
var redirectionAddress = "/";
|
||||
var loginAddress = "/api/auth/login";
|
||||
$(".checkbox").checkbox();
|
||||
|
112
src/web/main.css
112
src/web/main.css
@ -7,6 +7,7 @@
|
||||
--theme_green: #3c9c63;
|
||||
--theme_fcolor: #979797;
|
||||
--theme_advance: #f8f8f9;
|
||||
--theme_background: linear-gradient(60deg, rgb(84, 58, 183) 0%, rgb(0, 172, 193) 100%);
|
||||
}
|
||||
body{
|
||||
background-color:#f6f6f6;
|
||||
@ -31,14 +32,14 @@ body{
|
||||
padding: 0.4em;
|
||||
padding-left: 1.2em;
|
||||
padding-right: 1.2em;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #ffffff;
|
||||
margin-bottom: 1em;
|
||||
|
||||
border-bottom: 1px solid rgb(226, 226, 226);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
box-shadow: 0px 1px 5px 0px rgba(38,38,38,0.26);
|
||||
|
||||
}
|
||||
|
||||
.menubar .logo{
|
||||
@ -82,13 +83,21 @@ body{
|
||||
}
|
||||
|
||||
.serverstatusWrapper{
|
||||
padding-right: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.statisticWrapper{
|
||||
margin-top: 1em;
|
||||
padding-left: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
padding-right: 1em !important;
|
||||
}
|
||||
|
||||
.statisticWrapper .statustab{
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* Message Box */
|
||||
#messageBox{
|
||||
position: fixed;
|
||||
@ -204,14 +213,6 @@ body{
|
||||
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
#serverstatus{
|
||||
border-top-left-radius: 1em !important;
|
||||
}
|
||||
|
||||
.greybackground.statustab{
|
||||
border-top-right-radius: 1em !important;
|
||||
}
|
||||
|
||||
.standardContainer{
|
||||
padding-left: 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 {
|
||||
position: fixed;
|
||||
display: inline-block;
|
||||
@ -257,19 +269,10 @@ body{
|
||||
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{
|
||||
border-bottom: 0px solid transparent !important;
|
||||
}
|
||||
|
||||
.greybackground.statustab{
|
||||
border-top-right-radius: 0em !important;
|
||||
padding: 2em 2em !important;
|
||||
}
|
||||
|
||||
.standardContainer{
|
||||
padding-left: 1.2em;
|
||||
padding-right: 1.2em;
|
||||
@ -301,8 +304,12 @@ body{
|
||||
color: #5e5d5d;
|
||||
}
|
||||
|
||||
.ui.segment{
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.ui.secondary.vertical.menu .active.item{
|
||||
background-color: #414141;
|
||||
background: var(--theme_background);
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
}
|
||||
@ -311,11 +318,20 @@ body{
|
||||
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{
|
||||
color: #417ac1 !important;
|
||||
}
|
||||
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 50%;
|
||||
@ -327,52 +343,26 @@ body{
|
||||
*/
|
||||
#serverstatus{
|
||||
height: 100%;
|
||||
border-radius: 1em;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#statusTitle{
|
||||
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{
|
||||
background-color: #fefefe !important;
|
||||
border-right: 5px solid #3d9c64;
|
||||
background: linear-gradient(60deg, #27e7ff, #00ca52);
|
||||
}
|
||||
#serverstatus.green .sub.header{
|
||||
color: rgb(224, 224, 224);
|
||||
}
|
||||
#serverstatus.green i,
|
||||
#serverstatus.green #statusTitle{
|
||||
color: #3d9c64;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
#serverstatus.green #statusText{
|
||||
color: #2c583d;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
|
||||
@ -381,22 +371,20 @@ body{
|
||||
}
|
||||
|
||||
#serverstatus:not(.green){
|
||||
background-color: white !important;
|
||||
background-image: url("img/plant.png");
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 100%;
|
||||
background: linear-gradient(215deg, rgba(38,60,71,1) 13%, rgba(2,3,42,1) 84%);
|
||||
}
|
||||
|
||||
#serverstatus:not(.green) #statusTitle,
|
||||
#serverstatus:not(.green) i,
|
||||
#serverstatus:not(.green) .sub.header{
|
||||
color: #4c4c4c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.statustab{
|
||||
min-height: 5.5em;
|
||||
margin: 1em;
|
||||
border-radius: 1em !important;
|
||||
}
|
||||
|
||||
#summaryTotalCount{
|
||||
@ -467,7 +455,7 @@ body{
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
background-color: #3d9c64;
|
||||
background-color: white;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
100% {
|
||||
|
@ -7,12 +7,14 @@
|
||||
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||
<title>Account Reset | Zoraxy</title>
|
||||
<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/semantic/semantic.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background: rgb(245,245,245);
|
||||
background: linear-gradient(28deg, rgba(245,245,245,1) 63%, rgba(255,255,255,1) 100%);
|
||||
background: rgb(38,60,71);
|
||||
background: linear-gradient(215deg, rgba(38,60,71,1) 13%, rgba(2,3,42,1) 84%);
|
||||
}
|
||||
|
||||
.background{
|
||||
@ -34,23 +36,6 @@
|
||||
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{
|
||||
color: #9f3a38;
|
||||
margin-top: 1em;
|
||||
@ -61,18 +46,102 @@
|
||||
.backBtn{
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
left: 1em;
|
||||
margin-top: -4em;
|
||||
left: 2em;
|
||||
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>
|
||||
</head>
|
||||
<body>
|
||||
<div class="background"></div>
|
||||
<div id="loginForm" class="ui middle aligned center aligned grid">
|
||||
<div id="loginForm" class="ui middle aligned center aligned grid" data-aos="fade-up">
|
||||
<div class="column">
|
||||
<a class="backBtn" href="/">
|
||||
<i class="huge black chevron circle left icon"></i>
|
||||
</a>
|
||||
<form class="ui large form">
|
||||
<div class="ui basic segment">
|
||||
<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">
|
||||
</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;">
|
||||
<i class="red remove icon"></i> Unknown Error Occured
|
||||
</div>
|
||||
@ -107,12 +176,36 @@
|
||||
<a href="#" id="resendEmailLink" onclick="sendResetAccountEmail();">Resend Email</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<small>Proudly powered by Zoraxy</small>
|
||||
</form>
|
||||
<a class="backBtn" href="/">
|
||||
<i class="big chevron circle left icon" style="color: #121d37;"></i>
|
||||
</a>
|
||||
</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>
|
||||
AOS.init();
|
||||
|
||||
|
||||
var redirectionAddress = "/";
|
||||
var loginAddress = "/api/auth/login";
|
||||
$(".checkbox").checkbox();
|
||||
@ -155,6 +248,12 @@
|
||||
var token = $('#token').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
|
||||
$.post('/api/account/new', { username: username, token: token, newpw: newPassword })
|
||||
.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">
|
||||
</div>
|
||||
<div class="field" >
|
||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue 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>
|
||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="green add icon"></i> Add Credential</button>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</div>
|
||||
@ -69,7 +68,7 @@
|
||||
<small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
|
||||
</div>
|
||||
<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 class="field">
|
||||
<div class="ui basic message">
|
||||
@ -99,7 +98,7 @@
|
||||
let payloadHash = window.location.hash.substr(1);
|
||||
try{
|
||||
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
|
||||
loadBasicAuthCredentials(payloadHash.ept, payloadHash.ep);
|
||||
loadBasicAuthCredentials(payloadHash.ep);
|
||||
$("#epname").text(payloadHash.ep);
|
||||
editingEndpoint = payloadHash;
|
||||
}catch(ex){
|
||||
@ -107,13 +106,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
function loadBasicAuthCredentials(endpointType, uuid){
|
||||
function loadBasicAuthCredentials(uuid){
|
||||
$.ajax({
|
||||
url: "/api/proxy/updateCredentials",
|
||||
method: "GET",
|
||||
data: {
|
||||
ep: uuid,
|
||||
ptype: endpointType
|
||||
},
|
||||
success: function(data){
|
||||
//Push the existing account to list
|
||||
@ -163,6 +161,9 @@
|
||||
|
||||
// Update the table body with the credentials
|
||||
updateEditingCredentialList();
|
||||
|
||||
//Save the table
|
||||
saveCredentials();
|
||||
}
|
||||
|
||||
function addExceptionPath(){
|
||||
@ -175,7 +176,6 @@
|
||||
$.ajax({
|
||||
url: "/api/proxy/auth/exceptions/add",
|
||||
data:{
|
||||
ptype: editingEndpoint.ept,
|
||||
ep: editingEndpoint.ep,
|
||||
prefix: newExclusionPathMatchingPrefix
|
||||
},
|
||||
@ -197,7 +197,6 @@
|
||||
$.ajax({
|
||||
url: "/api/proxy/auth/exceptions/delete",
|
||||
data:{
|
||||
ptype: editingEndpoint.ept,
|
||||
ep: editingEndpoint.ep,
|
||||
prefix: matchingPrefix
|
||||
},
|
||||
@ -271,6 +270,8 @@
|
||||
|
||||
// Update the table body
|
||||
updateEditingCredentialList();
|
||||
|
||||
saveCredentials();
|
||||
}
|
||||
|
||||
function alreadyExists(username){
|
||||
@ -293,7 +294,6 @@
|
||||
method: "POST",
|
||||
data: {
|
||||
ep: editingEndpoint.ep,
|
||||
ptype: editingEndpoint.ept,
|
||||
creds: JSON.stringify(editingCredentials)
|
||||
},
|
||||
success: function(data){
|
||||
|
@ -119,11 +119,10 @@ func UpdateUptimeMonitorTargets() {
|
||||
|
||||
// Generate uptime monitor targets from reverse proxy rules
|
||||
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
||||
subds := dp.GetSDProxyEndpointsAsMap()
|
||||
vdirs := dp.GetVDProxyEndpointsAsMap()
|
||||
hosts := dp.GetProxyEndpointsAsMap()
|
||||
|
||||
UptimeTargets := []*uptime.Target{}
|
||||
for subd, target := range subds {
|
||||
for subd, target := range hosts {
|
||||
url := "http://" + target.Domain
|
||||
protocol := "http"
|
||||
if target.RequireTLS {
|
||||
@ -139,21 +138,6 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
|
||||
})
|
||||
}
|
||||
|
||||
for vdir, target := range vdirs {
|
||||
url := "http://" + target.Domain
|
||||
protocol := "http"
|
||||
if target.RequireTLS {
|
||||
url = "https://" + target.Domain
|
||||
protocol = "https"
|
||||
}
|
||||
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
||||
ID: vdir,
|
||||
Name: "*" + vdir,
|
||||
URL: url,
|
||||
Protocol: protocol,
|
||||
})
|
||||
}
|
||||
|
||||
return UptimeTargets
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user