mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
commit
0eb0696670
@ -33,6 +33,7 @@ A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
|
|||||||
- Basic single-admin management mode
|
- Basic single-admin management mode
|
||||||
- External permission management system for easy system integration
|
- External permission management system for easy system integration
|
||||||
- SMTP config for password reset
|
- SMTP config for password reset
|
||||||
|
- Dark Theme Mode
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
@ -102,6 +103,8 @@ Usage of zoraxy:
|
|||||||
Enable auto config upgrade if breaking change is detected (default true)
|
Enable auto config upgrade if breaking change is detected (default true)
|
||||||
-docker
|
-docker
|
||||||
Run Zoraxy in docker compatibility mode
|
Run Zoraxy in docker compatibility mode
|
||||||
|
-earlyrenew int
|
||||||
|
Number of days to early renew a soon expiring certificate (days) (default 30)
|
||||||
-fastgeoip
|
-fastgeoip
|
||||||
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
|
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
|
||||||
-mdns
|
-mdns
|
||||||
@ -119,7 +122,7 @@ Usage of zoraxy:
|
|||||||
-webfm
|
-webfm
|
||||||
Enable web file manager for static web server root folder (default true)
|
Enable web file manager for static web server root folder (default true)
|
||||||
-webroot string
|
-webroot string
|
||||||
Static web server root folder. Only allow change in start parameters (default "./www")
|
Static web server root folder. Only allow chnage in start paramters (default "./www")
|
||||||
-ztauth string
|
-ztauth string
|
||||||
ZeroTier authtoken for the local node
|
ZeroTier authtoken for the local node
|
||||||
-ztport int
|
-ztport int
|
||||||
|
@ -230,7 +230,17 @@ func handleCountryBlacklistAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.AddCountryCodeToBlackList(countryCode, comment)
|
//Check if the country code contains comma, if yes, split it
|
||||||
|
if strings.Contains(countryCode, ",") {
|
||||||
|
codes := strings.Split(countryCode, ",")
|
||||||
|
for _, code := range codes {
|
||||||
|
code = strings.TrimSpace(code)
|
||||||
|
rule.AddCountryCodeToBlackList(code, comment)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countryCode = strings.TrimSpace(countryCode)
|
||||||
|
rule.AddCountryCodeToBlackList(countryCode, comment)
|
||||||
|
}
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@ -254,7 +264,17 @@ func handleCountryBlacklistRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.RemoveCountryCodeFromBlackList(countryCode)
|
//Check if the country code contains comma, if yes, split it
|
||||||
|
if strings.Contains(countryCode, ",") {
|
||||||
|
codes := strings.Split(countryCode, ",")
|
||||||
|
for _, code := range codes {
|
||||||
|
code = strings.TrimSpace(code)
|
||||||
|
rule.RemoveCountryCodeFromBlackList(code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countryCode = strings.TrimSpace(countryCode)
|
||||||
|
rule.RemoveCountryCodeFromBlackList(countryCode)
|
||||||
|
}
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@ -397,7 +417,17 @@ func handleCountryWhitelistAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
p := bluemonday.StrictPolicy()
|
p := bluemonday.StrictPolicy()
|
||||||
comment = p.Sanitize(comment)
|
comment = p.Sanitize(comment)
|
||||||
|
|
||||||
rule.AddCountryCodeToWhitelist(countryCode, comment)
|
//Check if the country code contains comma, if yes, split it
|
||||||
|
if strings.Contains(countryCode, ",") {
|
||||||
|
codes := strings.Split(countryCode, ",")
|
||||||
|
for _, code := range codes {
|
||||||
|
code = strings.TrimSpace(code)
|
||||||
|
rule.AddCountryCodeToWhitelist(code, comment)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countryCode = strings.TrimSpace(countryCode)
|
||||||
|
rule.AddCountryCodeToWhitelist(countryCode, comment)
|
||||||
|
}
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@ -420,7 +450,17 @@ func handleCountryWhitelistRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.RemoveCountryCodeFromWhitelist(countryCode)
|
//Check if the country code contains comma, if yes, split it
|
||||||
|
if strings.Contains(countryCode, ",") {
|
||||||
|
codes := strings.Split(countryCode, ",")
|
||||||
|
for _, code := range codes {
|
||||||
|
code = strings.TrimSpace(code)
|
||||||
|
rule.RemoveCountryCodeFromWhitelist(code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
countryCode = strings.TrimSpace(countryCode)
|
||||||
|
rule.RemoveCountryCodeFromWhitelist(countryCode)
|
||||||
|
}
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
20
src/acme.go
20
src/acme.go
@ -41,6 +41,20 @@ func initACME() *acme.ACMEHandler {
|
|||||||
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port), sysdb, SystemWideLogger)
|
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port), sysdb, SystemWideLogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restart ACME handler and auto renewer
|
||||||
|
func restartACMEHandler() {
|
||||||
|
SystemWideLogger.Println("Restarting ACME handler")
|
||||||
|
//Clos the current handler and auto renewer
|
||||||
|
acmeHandler.Close()
|
||||||
|
acmeAutoRenewer.Close()
|
||||||
|
acmeDeregisterSpecialRoutingRule()
|
||||||
|
|
||||||
|
//Reinit the handler with a new random port
|
||||||
|
acmeHandler = initACME()
|
||||||
|
|
||||||
|
acmeRegisterSpecialRoutingRule()
|
||||||
|
}
|
||||||
|
|
||||||
// create the special routing rule for ACME
|
// create the special routing rule for ACME
|
||||||
func acmeRegisterSpecialRoutingRule() {
|
func acmeRegisterSpecialRoutingRule() {
|
||||||
SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport())
|
SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport())
|
||||||
@ -82,6 +96,12 @@ func acmeRegisterSpecialRoutingRule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove the special routing rule for ACME
|
||||||
|
func acmeDeregisterSpecialRoutingRule() {
|
||||||
|
SystemWideLogger.Println("Removing ACME routing rule")
|
||||||
|
dynamicProxyRouter.RemoveRoutingRule("acme-autorenew")
|
||||||
|
}
|
||||||
|
|
||||||
// This function check if the renew setup is satisfied. If not, toggle them automatically
|
// This function check if the renew setup is satisfied. If not, toggle them automatically
|
||||||
func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
|
func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
|
||||||
isForceHttpsRedirectEnabledOriginally := false
|
isForceHttpsRedirectEnabledOriginally := false
|
||||||
|
264
src/api.go
264
src/api.go
@ -8,6 +8,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/acme/acmedns"
|
"imuslab.com/zoraxy/mod/acme/acmedns"
|
||||||
"imuslab.com/zoraxy/mod/acme/acmewizard"
|
"imuslab.com/zoraxy/mod/acme/acmewizard"
|
||||||
"imuslab.com/zoraxy/mod/auth"
|
"imuslab.com/zoraxy/mod/auth"
|
||||||
|
"imuslab.com/zoraxy/mod/dynamicproxy/domainsniff"
|
||||||
"imuslab.com/zoraxy/mod/ipscan"
|
"imuslab.com/zoraxy/mod/ipscan"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
"imuslab.com/zoraxy/mod/netutils"
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
@ -18,34 +19,11 @@ import (
|
|||||||
API.go
|
API.go
|
||||||
|
|
||||||
This file contains all the API called by the web management interface
|
This file contains all the API called by the web management interface
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var requireAuth = true
|
// Register the APIs for HTTP proxy management functions
|
||||||
|
func RegisterHTTPProxyAPIs(authRouter *auth.RouterDef) {
|
||||||
func initAPIs(targetMux *http.ServeMux) {
|
/* Reverse Proxy Settings & Status */
|
||||||
authRouter := auth.NewManagedHTTPRouter(auth.RouterOption{
|
|
||||||
AuthAgent: authAgent,
|
|
||||||
RequireAuth: requireAuth,
|
|
||||||
TargetMux: targetMux,
|
|
||||||
DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.Error(w, "401 - Unauthorized", http.StatusUnauthorized)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
//Register the standard web services urls
|
|
||||||
fs := http.FileServer(http.FS(webres))
|
|
||||||
if development {
|
|
||||||
fs = http.FileServer(http.Dir("web/"))
|
|
||||||
}
|
|
||||||
//Add a layer of middleware for advance control
|
|
||||||
advHandler := FSHandler(fs)
|
|
||||||
targetMux.Handle("/", advHandler)
|
|
||||||
|
|
||||||
//Authentication APIs
|
|
||||||
registerAuthAPIs(requireAuth, targetMux)
|
|
||||||
|
|
||||||
//Reverse proxy
|
|
||||||
authRouter.HandleFunc("/api/proxy/enable", ReverseProxyHandleOnOff)
|
authRouter.HandleFunc("/api/proxy/enable", ReverseProxyHandleOnOff)
|
||||||
authRouter.HandleFunc("/api/proxy/add", ReverseProxyHandleAddEndpoint)
|
authRouter.HandleFunc("/api/proxy/add", ReverseProxyHandleAddEndpoint)
|
||||||
authRouter.HandleFunc("/api/proxy/status", ReverseProxyStatus)
|
authRouter.HandleFunc("/api/proxy/status", ReverseProxyStatus)
|
||||||
@ -56,24 +34,24 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias)
|
authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias)
|
||||||
authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
|
authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
|
||||||
authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
|
authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
|
||||||
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
authRouter.HandleFunc("/api/proxy/tlscheck", domainsniff.HandleCheckSiteSupportTLS)
|
||||||
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
||||||
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
||||||
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
||||||
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
||||||
authRouter.HandleFunc("/api/proxy/developmentMode", HandleDevelopmentModeChange)
|
authRouter.HandleFunc("/api/proxy/developmentMode", HandleDevelopmentModeChange)
|
||||||
//Reverse proxy upstream (load balance) APIs
|
/* Reverse proxy upstream (load balance) */
|
||||||
authRouter.HandleFunc("/api/proxy/upstream/list", ReverseProxyUpstreamList)
|
authRouter.HandleFunc("/api/proxy/upstream/list", ReverseProxyUpstreamList)
|
||||||
authRouter.HandleFunc("/api/proxy/upstream/add", ReverseProxyUpstreamAdd)
|
authRouter.HandleFunc("/api/proxy/upstream/add", ReverseProxyUpstreamAdd)
|
||||||
authRouter.HandleFunc("/api/proxy/upstream/setPriority", ReverseProxyUpstreamSetPriority)
|
authRouter.HandleFunc("/api/proxy/upstream/setPriority", ReverseProxyUpstreamSetPriority)
|
||||||
authRouter.HandleFunc("/api/proxy/upstream/update", ReverseProxyUpstreamUpdate)
|
authRouter.HandleFunc("/api/proxy/upstream/update", ReverseProxyUpstreamUpdate)
|
||||||
authRouter.HandleFunc("/api/proxy/upstream/remove", ReverseProxyUpstreamDelete)
|
authRouter.HandleFunc("/api/proxy/upstream/remove", ReverseProxyUpstreamDelete)
|
||||||
//Reverse proxy virtual directory APIs
|
/* Reverse proxy virtual directory */
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/list", ReverseProxyListVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/list", ReverseProxyListVdir)
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/add", ReverseProxyAddVdir)
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/del", ReverseProxyDeleteVdir)
|
||||||
authRouter.HandleFunc("/api/proxy/vdir/edit", ReverseProxyEditVdir)
|
authRouter.HandleFunc("/api/proxy/vdir/edit", ReverseProxyEditVdir)
|
||||||
//Reverse proxy user define header apis
|
/* Reverse proxy user-defined header */
|
||||||
authRouter.HandleFunc("/api/proxy/header/list", HandleCustomHeaderList)
|
authRouter.HandleFunc("/api/proxy/header/list", HandleCustomHeaderList)
|
||||||
authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd)
|
authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd)
|
||||||
authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove)
|
authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove)
|
||||||
@ -81,12 +59,14 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/proxy/header/handleHopByHop", HandleHopByHop)
|
authRouter.HandleFunc("/api/proxy/header/handleHopByHop", HandleHopByHop)
|
||||||
authRouter.HandleFunc("/api/proxy/header/handleHostOverwrite", HandleHostOverwrite)
|
authRouter.HandleFunc("/api/proxy/header/handleHostOverwrite", HandleHostOverwrite)
|
||||||
authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy)
|
authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy)
|
||||||
//Reverse proxy auth related APIs
|
/* Reverse proxy auth related */
|
||||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
||||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
||||||
authRouter.HandleFunc("/api/proxy/auth/exceptions/delete", RemoveProxyBasicAuthExceptionPaths)
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/delete", RemoveProxyBasicAuthExceptionPaths)
|
||||||
|
}
|
||||||
|
|
||||||
//TLS / SSL config
|
// Register the APIs for TLS / SSL certificate management functions
|
||||||
|
func RegisterTLSAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
||||||
authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest)
|
authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest)
|
||||||
authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
|
authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
|
||||||
@ -95,8 +75,10 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/cert/listdomains", handleListDomains)
|
authRouter.HandleFunc("/api/cert/listdomains", handleListDomains)
|
||||||
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
|
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
|
||||||
authRouter.HandleFunc("/api/cert/delete", handleCertRemove)
|
authRouter.HandleFunc("/api/cert/delete", handleCertRemove)
|
||||||
|
}
|
||||||
|
|
||||||
//SSO and Oauth
|
// Register the APIs for SSO and Oauth functions, WIP
|
||||||
|
func RegisterSSOAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/sso/status", ssoHandler.HandleSSOStatus)
|
authRouter.HandleFunc("/api/sso/status", ssoHandler.HandleSSOStatus)
|
||||||
authRouter.HandleFunc("/api/sso/enable", ssoHandler.HandleSSOEnable)
|
authRouter.HandleFunc("/api/sso/enable", ssoHandler.HandleSSOEnable)
|
||||||
authRouter.HandleFunc("/api/sso/setPort", ssoHandler.HandlePortChange)
|
authRouter.HandleFunc("/api/sso/setPort", ssoHandler.HandlePortChange)
|
||||||
@ -110,48 +92,67 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/sso/user/add", ssoHandler.HandleAddUser)
|
authRouter.HandleFunc("/api/sso/user/add", ssoHandler.HandleAddUser)
|
||||||
authRouter.HandleFunc("/api/sso/user/edit", ssoHandler.HandleEditUser)
|
authRouter.HandleFunc("/api/sso/user/edit", ssoHandler.HandleEditUser)
|
||||||
authRouter.HandleFunc("/api/sso/user/remove", ssoHandler.HandleRemoveUser)
|
authRouter.HandleFunc("/api/sso/user/remove", ssoHandler.HandleRemoveUser)
|
||||||
|
}
|
||||||
|
|
||||||
//Redirection config
|
// Register the APIs for redirection rules management functions
|
||||||
|
func RegisterRedirectionAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/redirect/list", handleListRedirectionRules)
|
authRouter.HandleFunc("/api/redirect/list", handleListRedirectionRules)
|
||||||
authRouter.HandleFunc("/api/redirect/add", handleAddRedirectionRule)
|
authRouter.HandleFunc("/api/redirect/add", handleAddRedirectionRule)
|
||||||
authRouter.HandleFunc("/api/redirect/delete", handleDeleteRedirectionRule)
|
authRouter.HandleFunc("/api/redirect/delete", handleDeleteRedirectionRule)
|
||||||
authRouter.HandleFunc("/api/redirect/regex", handleToggleRedirectRegexpSupport)
|
authRouter.HandleFunc("/api/redirect/regex", handleToggleRedirectRegexpSupport)
|
||||||
|
}
|
||||||
|
|
||||||
//Access Rules API
|
// Register the APIs for access rules management functions
|
||||||
|
func RegisterAccessRuleAPIs(authRouter *auth.RouterDef) {
|
||||||
|
/* Access Rules Settings & Status */
|
||||||
authRouter.HandleFunc("/api/access/list", handleListAccessRules)
|
authRouter.HandleFunc("/api/access/list", handleListAccessRules)
|
||||||
authRouter.HandleFunc("/api/access/attach", handleAttachRuleToHost)
|
authRouter.HandleFunc("/api/access/attach", handleAttachRuleToHost)
|
||||||
authRouter.HandleFunc("/api/access/create", handleCreateAccessRule)
|
authRouter.HandleFunc("/api/access/create", handleCreateAccessRule)
|
||||||
authRouter.HandleFunc("/api/access/remove", handleRemoveAccessRule)
|
authRouter.HandleFunc("/api/access/remove", handleRemoveAccessRule)
|
||||||
authRouter.HandleFunc("/api/access/update", handleUpadateAccessRule)
|
authRouter.HandleFunc("/api/access/update", handleUpadateAccessRule)
|
||||||
//Blacklist APIs
|
/* Blacklist */
|
||||||
authRouter.HandleFunc("/api/blacklist/list", handleListBlacklisted)
|
authRouter.HandleFunc("/api/blacklist/list", handleListBlacklisted)
|
||||||
authRouter.HandleFunc("/api/blacklist/country/add", handleCountryBlacklistAdd)
|
authRouter.HandleFunc("/api/blacklist/country/add", handleCountryBlacklistAdd)
|
||||||
authRouter.HandleFunc("/api/blacklist/country/remove", handleCountryBlacklistRemove)
|
authRouter.HandleFunc("/api/blacklist/country/remove", handleCountryBlacklistRemove)
|
||||||
authRouter.HandleFunc("/api/blacklist/ip/add", handleIpBlacklistAdd)
|
authRouter.HandleFunc("/api/blacklist/ip/add", handleIpBlacklistAdd)
|
||||||
authRouter.HandleFunc("/api/blacklist/ip/remove", handleIpBlacklistRemove)
|
authRouter.HandleFunc("/api/blacklist/ip/remove", handleIpBlacklistRemove)
|
||||||
authRouter.HandleFunc("/api/blacklist/enable", handleBlacklistEnable)
|
authRouter.HandleFunc("/api/blacklist/enable", handleBlacklistEnable)
|
||||||
//Whitelist APIs
|
/* Whitelist */
|
||||||
authRouter.HandleFunc("/api/whitelist/list", handleListWhitelisted)
|
authRouter.HandleFunc("/api/whitelist/list", handleListWhitelisted)
|
||||||
authRouter.HandleFunc("/api/whitelist/country/add", handleCountryWhitelistAdd)
|
authRouter.HandleFunc("/api/whitelist/country/add", handleCountryWhitelistAdd)
|
||||||
authRouter.HandleFunc("/api/whitelist/country/remove", handleCountryWhitelistRemove)
|
authRouter.HandleFunc("/api/whitelist/country/remove", handleCountryWhitelistRemove)
|
||||||
authRouter.HandleFunc("/api/whitelist/ip/add", handleIpWhitelistAdd)
|
authRouter.HandleFunc("/api/whitelist/ip/add", handleIpWhitelistAdd)
|
||||||
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
|
||||||
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
|
||||||
|
}
|
||||||
|
|
||||||
//Path Blocker APIs
|
// Register the APIs for path blocking rules management functions, WIP
|
||||||
|
func RegisterPathRuleAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/pathrule/add", pathRuleHandler.HandleAddBlockingPath)
|
authRouter.HandleFunc("/api/pathrule/add", pathRuleHandler.HandleAddBlockingPath)
|
||||||
authRouter.HandleFunc("/api/pathrule/list", pathRuleHandler.HandleListBlockingPath)
|
authRouter.HandleFunc("/api/pathrule/list", pathRuleHandler.HandleListBlockingPath)
|
||||||
authRouter.HandleFunc("/api/pathrule/remove", pathRuleHandler.HandleRemoveBlockingPath)
|
authRouter.HandleFunc("/api/pathrule/remove", pathRuleHandler.HandleRemoveBlockingPath)
|
||||||
|
}
|
||||||
|
|
||||||
//Statistic & uptime monitoring API
|
// Register the APIs statistic anlysis and uptime monitoring functions
|
||||||
|
func RegisterStatisticalAPIs(authRouter *auth.RouterDef) {
|
||||||
|
/* Traffic Summary */
|
||||||
authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
|
authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
|
||||||
authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
|
authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)
|
||||||
authRouter.HandleFunc("/api/stats/netstat", netstatBuffers.HandleGetNetworkInterfaceStats)
|
authRouter.HandleFunc("/api/stats/netstat", netstatBuffers.HandleGetNetworkInterfaceStats)
|
||||||
authRouter.HandleFunc("/api/stats/netstatgraph", netstatBuffers.HandleGetBufferedNetworkInterfaceStats)
|
authRouter.HandleFunc("/api/stats/netstatgraph", netstatBuffers.HandleGetBufferedNetworkInterfaceStats)
|
||||||
authRouter.HandleFunc("/api/stats/listnic", netstat.HandleListNetworkInterfaces)
|
authRouter.HandleFunc("/api/stats/listnic", netstat.HandleListNetworkInterfaces)
|
||||||
|
/* Zoraxy Analytic */
|
||||||
|
authRouter.HandleFunc("/api/analytic/list", AnalyticLoader.HandleSummaryList)
|
||||||
|
authRouter.HandleFunc("/api/analytic/load", AnalyticLoader.HandleLoadTargetDaySummary)
|
||||||
|
authRouter.HandleFunc("/api/analytic/loadRange", AnalyticLoader.HandleLoadTargetRangeSummary)
|
||||||
|
authRouter.HandleFunc("/api/analytic/exportRange", AnalyticLoader.HandleRangeExport)
|
||||||
|
authRouter.HandleFunc("/api/analytic/resetRange", AnalyticLoader.HandleRangeReset)
|
||||||
|
/* UpTime Monitor */
|
||||||
authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
|
authRouter.HandleFunc("/api/utm/list", HandleUptimeMonitorListing)
|
||||||
|
}
|
||||||
|
|
||||||
//Global Area Network APIs
|
// Register the APIs for Global Area Network management functions, Will be moving to plugin soon
|
||||||
|
func RegisterGANAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/gan/network/info", ganManager.HandleGetNodeID)
|
authRouter.HandleFunc("/api/gan/network/info", ganManager.HandleGetNodeID)
|
||||||
authRouter.HandleFunc("/api/gan/network/add", ganManager.HandleAddNetwork)
|
authRouter.HandleFunc("/api/gan/network/add", ganManager.HandleAddNetwork)
|
||||||
authRouter.HandleFunc("/api/gan/network/remove", ganManager.HandleRemoveNetwork)
|
authRouter.HandleFunc("/api/gan/network/remove", ganManager.HandleRemoveNetwork)
|
||||||
@ -166,8 +167,10 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/gan/members/name", ganManager.HandleMemberNaming)
|
authRouter.HandleFunc("/api/gan/members/name", ganManager.HandleMemberNaming)
|
||||||
authRouter.HandleFunc("/api/gan/members/authorize", ganManager.HandleMemberAuthorization)
|
authRouter.HandleFunc("/api/gan/members/authorize", ganManager.HandleMemberAuthorization)
|
||||||
authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete)
|
authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete)
|
||||||
|
}
|
||||||
|
|
||||||
//Stream (TCP / UDP) Proxy
|
// Register the APIs for Stream (TCP / UDP) Proxy management functions
|
||||||
|
func RegisterStreamProxyAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig)
|
authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig)
|
||||||
authRouter.HandleFunc("/api/streamprox/config/edit", streamProxyManager.HandleEditProxyConfigs)
|
authRouter.HandleFunc("/api/streamprox/config/edit", streamProxyManager.HandleEditProxyConfigs)
|
||||||
authRouter.HandleFunc("/api/streamprox/config/list", streamProxyManager.HandleListConfigs)
|
authRouter.HandleFunc("/api/streamprox/config/list", streamProxyManager.HandleListConfigs)
|
||||||
@ -175,19 +178,57 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/streamprox/config/stop", streamProxyManager.HandleStopProxy)
|
authRouter.HandleFunc("/api/streamprox/config/stop", streamProxyManager.HandleStopProxy)
|
||||||
authRouter.HandleFunc("/api/streamprox/config/delete", streamProxyManager.HandleRemoveProxy)
|
authRouter.HandleFunc("/api/streamprox/config/delete", streamProxyManager.HandleRemoveProxy)
|
||||||
authRouter.HandleFunc("/api/streamprox/config/status", streamProxyManager.HandleGetProxyStatus)
|
authRouter.HandleFunc("/api/streamprox/config/status", streamProxyManager.HandleGetProxyStatus)
|
||||||
|
}
|
||||||
|
|
||||||
//mDNS APIs
|
// Register the APIs for mDNS service management functions
|
||||||
|
func RegisterMDNSAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
|
authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
|
||||||
authRouter.HandleFunc("/api/mdns/discover", HandleMdnsScanning)
|
authRouter.HandleFunc("/api/mdns/discover", HandleMdnsScanning)
|
||||||
|
}
|
||||||
|
|
||||||
//Zoraxy Analytic
|
// Register the APIs for ACME and Auto Renewer management functions
|
||||||
authRouter.HandleFunc("/api/analytic/list", AnalyticLoader.HandleSummaryList)
|
func RegisterACMEAndAutoRenewerAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/analytic/load", AnalyticLoader.HandleLoadTargetDaySummary)
|
/* ACME Core */
|
||||||
authRouter.HandleFunc("/api/analytic/loadRange", AnalyticLoader.HandleLoadTargetRangeSummary)
|
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
|
||||||
authRouter.HandleFunc("/api/analytic/exportRange", AnalyticLoader.HandleRangeExport)
|
authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
|
||||||
authRouter.HandleFunc("/api/analytic/resetRange", AnalyticLoader.HandleRangeReset)
|
/* Auto Renewer */
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/ca", HandleACMEPreferredCA)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/setEAB", acmeAutoRenewer.HanldeSetEAB)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/setDNS", acmeAutoRenewer.HandleSetDNS)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/renewPolicy", acmeAutoRenewer.HandleRenewPolicy)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/renewNow", acmeAutoRenewer.HandleRenewNow)
|
||||||
|
authRouter.HandleFunc("/api/acme/dns/providers", acmedns.HandleServeProvidersJson)
|
||||||
|
/* ACME Wizard */
|
||||||
|
authRouter.HandleFunc("/api/acme/wizard", acmewizard.HandleGuidedStepCheck)
|
||||||
|
}
|
||||||
|
|
||||||
//Network utilities
|
// Register the APIs for Static Web Server management functions
|
||||||
|
func RegisterStaticWebServerAPIs(authRouter *auth.RouterDef) {
|
||||||
|
/* Static Web Server Controls */
|
||||||
|
authRouter.HandleFunc("/api/webserv/status", staticWebServer.HandleGetStatus)
|
||||||
|
authRouter.HandleFunc("/api/webserv/start", staticWebServer.HandleStartServer)
|
||||||
|
authRouter.HandleFunc("/api/webserv/stop", staticWebServer.HandleStopServer)
|
||||||
|
authRouter.HandleFunc("/api/webserv/setPort", HandleStaticWebServerPortChange)
|
||||||
|
authRouter.HandleFunc("/api/webserv/setDirList", staticWebServer.SetEnableDirectoryListing)
|
||||||
|
/* File Manager */
|
||||||
|
if *allowWebFileManager {
|
||||||
|
authRouter.HandleFunc("/api/fs/list", staticWebServer.FileManager.HandleList)
|
||||||
|
authRouter.HandleFunc("/api/fs/upload", staticWebServer.FileManager.HandleUpload)
|
||||||
|
authRouter.HandleFunc("/api/fs/download", staticWebServer.FileManager.HandleDownload)
|
||||||
|
authRouter.HandleFunc("/api/fs/newFolder", staticWebServer.FileManager.HandleNewFolder)
|
||||||
|
authRouter.HandleFunc("/api/fs/copy", staticWebServer.FileManager.HandleFileCopy)
|
||||||
|
authRouter.HandleFunc("/api/fs/move", staticWebServer.FileManager.HandleFileMove)
|
||||||
|
authRouter.HandleFunc("/api/fs/properties", staticWebServer.FileManager.HandleFileProperties)
|
||||||
|
authRouter.HandleFunc("/api/fs/del", staticWebServer.FileManager.HandleFileDelete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the APIs for Network Utilities functions
|
||||||
|
func RegisterNetworkUtilsAPIs(authRouter *auth.RouterDef) {
|
||||||
authRouter.HandleFunc("/api/tools/ipscan", ipscan.HandleIpScan)
|
authRouter.HandleFunc("/api/tools/ipscan", ipscan.HandleIpScan)
|
||||||
authRouter.HandleFunc("/api/tools/portscan", ipscan.HandleScanPort)
|
authRouter.HandleFunc("/api/tools/portscan", ipscan.HandleScanPort)
|
||||||
authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
|
authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
|
||||||
@ -202,66 +243,10 @@ func initAPIs(targetMux *http.ServeMux) {
|
|||||||
authRouter.HandleFunc("/api/tools/smtp/test", HandleTestEmailSend)
|
authRouter.HandleFunc("/api/tools/smtp/test", HandleTestEmailSend)
|
||||||
authRouter.HandleFunc("/api/tools/fwdproxy/enable", forwardProxy.HandleToogle)
|
authRouter.HandleFunc("/api/tools/fwdproxy/enable", forwardProxy.HandleToogle)
|
||||||
authRouter.HandleFunc("/api/tools/fwdproxy/port", forwardProxy.HandlePort)
|
authRouter.HandleFunc("/api/tools/fwdproxy/port", forwardProxy.HandlePort)
|
||||||
|
|
||||||
//Account Reset
|
|
||||||
targetMux.HandleFunc("/api/account/reset", HandleAdminAccountResetEmail)
|
|
||||||
targetMux.HandleFunc("/api/account/new", HandleNewPasswordSetup)
|
|
||||||
|
|
||||||
//ACME & Auto Renewer
|
|
||||||
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
|
|
||||||
authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/ca", HandleACMEPreferredCA)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/setEAB", acmeAutoRenewer.HanldeSetEAB)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/setDNS", acmeAutoRenewer.HanldeSetDNS)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/renewPolicy", acmeAutoRenewer.HandleRenewPolicy)
|
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/renewNow", acmeAutoRenewer.HandleRenewNow)
|
|
||||||
authRouter.HandleFunc("/api/acme/dns/providers", acmedns.HandleServeProvidersJson)
|
|
||||||
authRouter.HandleFunc("/api/acme/wizard", acmewizard.HandleGuidedStepCheck) //ACME Wizard
|
|
||||||
|
|
||||||
//Static Web Server
|
|
||||||
authRouter.HandleFunc("/api/webserv/status", staticWebServer.HandleGetStatus)
|
|
||||||
authRouter.HandleFunc("/api/webserv/start", staticWebServer.HandleStartServer)
|
|
||||||
authRouter.HandleFunc("/api/webserv/stop", staticWebServer.HandleStopServer)
|
|
||||||
authRouter.HandleFunc("/api/webserv/setPort", HandleStaticWebServerPortChange)
|
|
||||||
authRouter.HandleFunc("/api/webserv/setDirList", staticWebServer.SetEnableDirectoryListing)
|
|
||||||
if *allowWebFileManager {
|
|
||||||
//Web Directory Manager file operation functions
|
|
||||||
authRouter.HandleFunc("/api/fs/list", staticWebServer.FileManager.HandleList)
|
|
||||||
authRouter.HandleFunc("/api/fs/upload", staticWebServer.FileManager.HandleUpload)
|
|
||||||
authRouter.HandleFunc("/api/fs/download", staticWebServer.FileManager.HandleDownload)
|
|
||||||
authRouter.HandleFunc("/api/fs/newFolder", staticWebServer.FileManager.HandleNewFolder)
|
|
||||||
authRouter.HandleFunc("/api/fs/copy", staticWebServer.FileManager.HandleFileCopy)
|
|
||||||
authRouter.HandleFunc("/api/fs/move", staticWebServer.FileManager.HandleFileMove)
|
|
||||||
authRouter.HandleFunc("/api/fs/properties", staticWebServer.FileManager.HandleFileProperties)
|
|
||||||
authRouter.HandleFunc("/api/fs/del", staticWebServer.FileManager.HandleFileDelete)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Docker UX Optimizations
|
|
||||||
authRouter.HandleFunc("/api/docker/available", DockerUXOptimizer.HandleDockerAvailable)
|
|
||||||
authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList)
|
|
||||||
|
|
||||||
//Others
|
|
||||||
targetMux.HandleFunc("/api/info/x", HandleZoraxyInfo)
|
|
||||||
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
|
|
||||||
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
|
||||||
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
|
||||||
authRouter.HandleFunc("/api/log/list", LogViewer.HandleListLog)
|
|
||||||
authRouter.HandleFunc("/api/log/read", LogViewer.HandleReadLog)
|
|
||||||
|
|
||||||
//Debug
|
|
||||||
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
|
|
||||||
|
|
||||||
//If you got APIs to add, append them here
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to renders Auth related APIs
|
// Register the APIs for Auth functions, due to scoping issue some functions are defined here
|
||||||
func registerAuthAPIs(requireAuth bool, targetMux *http.ServeMux) {
|
func RegisterAuthAPIs(requireAuth bool, targetMux *http.ServeMux) {
|
||||||
//Auth APIs
|
|
||||||
targetMux.HandleFunc("/api/auth/login", authAgent.HandleLogin)
|
targetMux.HandleFunc("/api/auth/login", authAgent.HandleLogin)
|
||||||
targetMux.HandleFunc("/api/auth/logout", authAgent.HandleLogout)
|
targetMux.HandleFunc("/api/auth/logout", authAgent.HandleLogout)
|
||||||
targetMux.HandleFunc("/api/auth/checkLogin", func(w http.ResponseWriter, r *http.Request) {
|
targetMux.HandleFunc("/api/auth/checkLogin", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -277,21 +262,17 @@ func registerAuthAPIs(requireAuth bool, targetMux *http.ServeMux) {
|
|||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
js, _ := json.Marshal(username)
|
js, _ := json.Marshal(username)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
})
|
})
|
||||||
targetMux.HandleFunc("/api/auth/userCount", func(w http.ResponseWriter, r *http.Request) {
|
targetMux.HandleFunc("/api/auth/userCount", func(w http.ResponseWriter, r *http.Request) {
|
||||||
uc := authAgent.GetUserCounts()
|
js, _ := json.Marshal(authAgent.GetUserCounts())
|
||||||
js, _ := json.Marshal(uc)
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
})
|
})
|
||||||
targetMux.HandleFunc("/api/auth/register", func(w http.ResponseWriter, r *http.Request) {
|
targetMux.HandleFunc("/api/auth/register", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if authAgent.GetUserCounts() == 0 {
|
if authAgent.GetUserCounts() == 0 {
|
||||||
//Allow register root admin
|
//Allow register root admin
|
||||||
authAgent.HandleRegisterWithoutEmail(w, r, func(username, reserved string) {
|
authAgent.HandleRegisterWithoutEmail(w, r, func(username, reserved string) {})
|
||||||
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
//This function is disabled
|
//This function is disabled
|
||||||
utils.SendErrorResponse(w, "Root management account already exists")
|
utils.SendErrorResponse(w, "Root management account already exists")
|
||||||
@ -332,5 +313,60 @@ func registerAuthAPIs(requireAuth bool, targetMux *http.ServeMux) {
|
|||||||
authAgent.UnregisterUser(username)
|
authAgent.UnregisterUser(username)
|
||||||
authAgent.CreateUserAccount(username, newPassword, "")
|
authAgent.CreateUserAccount(username, newPassword, "")
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register all the APIs */
|
||||||
|
func initAPIs(targetMux *http.ServeMux) {
|
||||||
|
authRouter := auth.NewManagedHTTPRouter(auth.RouterOption{
|
||||||
|
AuthAgent: authAgent,
|
||||||
|
RequireAuth: requireAuth,
|
||||||
|
TargetMux: targetMux,
|
||||||
|
DeniedHandler: func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "401 - Unauthorized", http.StatusUnauthorized)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//Register the standard web services urls
|
||||||
|
fs := http.FileServer(http.FS(webres))
|
||||||
|
if DEVELOPMENT_BUILD {
|
||||||
|
fs = http.FileServer(http.Dir("web/"))
|
||||||
|
}
|
||||||
|
//Add a layer of middleware for advance control
|
||||||
|
advHandler := FSHandler(fs)
|
||||||
|
targetMux.Handle("/", advHandler)
|
||||||
|
|
||||||
|
//Register the APIs
|
||||||
|
RegisterAuthAPIs(requireAuth, targetMux)
|
||||||
|
RegisterHTTPProxyAPIs(authRouter)
|
||||||
|
RegisterTLSAPIs(authRouter)
|
||||||
|
//RegisterSSOAPIs(authRouter)
|
||||||
|
RegisterRedirectionAPIs(authRouter)
|
||||||
|
RegisterAccessRuleAPIs(authRouter)
|
||||||
|
RegisterPathRuleAPIs(authRouter)
|
||||||
|
RegisterStatisticalAPIs(authRouter)
|
||||||
|
RegisterGANAPIs(authRouter)
|
||||||
|
RegisterStreamProxyAPIs(authRouter)
|
||||||
|
RegisterMDNSAPIs(authRouter)
|
||||||
|
RegisterNetworkUtilsAPIs(authRouter)
|
||||||
|
RegisterACMEAndAutoRenewerAPIs(authRouter)
|
||||||
|
RegisterStaticWebServerAPIs(authRouter)
|
||||||
|
|
||||||
|
//Account Reset
|
||||||
|
targetMux.HandleFunc("/api/account/reset", HandleAdminAccountResetEmail)
|
||||||
|
targetMux.HandleFunc("/api/account/new", HandleNewPasswordSetup)
|
||||||
|
|
||||||
|
//Docker UX Optimizations
|
||||||
|
authRouter.HandleFunc("/api/docker/available", DockerUXOptimizer.HandleDockerAvailable)
|
||||||
|
authRouter.HandleFunc("/api/docker/containers", DockerUXOptimizer.HandleDockerContainersList)
|
||||||
|
|
||||||
|
//Others
|
||||||
|
targetMux.HandleFunc("/api/info/x", HandleZoraxyInfo)
|
||||||
|
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
|
||||||
|
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
||||||
|
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
||||||
|
authRouter.HandleFunc("/api/log/list", LogViewer.HandleListLog)
|
||||||
|
authRouter.HandleFunc("/api/log/read", LogViewer.HandleReadLog)
|
||||||
|
|
||||||
|
//Debug
|
||||||
|
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,10 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Handle front-end toggling TLS mode
|
// Handle front-end toggling TLS mode
|
||||||
func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
currentTlsSetting := false
|
currentTlsSetting := true //Default to true
|
||||||
|
if dynamicProxyRouter.Option != nil {
|
||||||
|
currentTlsSetting = dynamicProxyRouter.Option.UseTls
|
||||||
|
}
|
||||||
if sysdb.KeyExists("settings", "usetls") {
|
if sysdb.KeyExists("settings", "usetls") {
|
||||||
sysdb.Read("settings", "usetls", ¤tTlsSetting)
|
sysdb.Read("settings", "usetls", ¤tTlsSetting)
|
||||||
}
|
}
|
||||||
|
138
src/def.go
Normal file
138
src/def.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
Type and flag definations
|
||||||
|
|
||||||
|
This file contains all the type and flag definations
|
||||||
|
Author: tobychui
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"flag"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/access"
|
||||||
|
"imuslab.com/zoraxy/mod/acme"
|
||||||
|
"imuslab.com/zoraxy/mod/auth"
|
||||||
|
"imuslab.com/zoraxy/mod/auth/sso"
|
||||||
|
"imuslab.com/zoraxy/mod/database"
|
||||||
|
"imuslab.com/zoraxy/mod/dockerux"
|
||||||
|
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
||||||
|
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||||
|
"imuslab.com/zoraxy/mod/email"
|
||||||
|
"imuslab.com/zoraxy/mod/forwardproxy"
|
||||||
|
"imuslab.com/zoraxy/mod/ganserv"
|
||||||
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logviewer"
|
||||||
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
|
"imuslab.com/zoraxy/mod/sshprox"
|
||||||
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
|
"imuslab.com/zoraxy/mod/streamproxy"
|
||||||
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
|
"imuslab.com/zoraxy/mod/uptime"
|
||||||
|
"imuslab.com/zoraxy/mod/webserv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
/* Build Constants */
|
||||||
|
SYSTEM_NAME = "Zoraxy"
|
||||||
|
SYSTEM_VERSION = "3.1.4"
|
||||||
|
DEVELOPMENT_BUILD = false /* Development: Set to false to use embedded web fs */
|
||||||
|
|
||||||
|
/* System Constants */
|
||||||
|
DATABASE_PATH = "sys.db"
|
||||||
|
TMP_FOLDER = "./tmp"
|
||||||
|
WEBSERV_DEFAULT_PORT = 5487
|
||||||
|
MDNS_HOSTNAME_PREFIX = "zoraxy_" /* Follow by node UUID */
|
||||||
|
MDNS_IDENTIFY_DEVICE_TYPE = "Network Gateway"
|
||||||
|
MDNS_IDENTIFY_DOMAIN = "zoraxy.aroz.org"
|
||||||
|
MDNS_IDENTIFY_VENDOR = "imuslab.com"
|
||||||
|
MDNS_SCAN_TIMEOUT = 30 /* Seconds */
|
||||||
|
MDNS_SCAN_UPDATE_INTERVAL = 15 /* Minutes */
|
||||||
|
GEODB_CACHE_CLEAR_INTERVAL = 15 /* Minutes */
|
||||||
|
ACME_AUTORENEW_CONFIG_PATH = "./conf/acme_conf.json"
|
||||||
|
CSRF_COOKIENAME = "zoraxy_csrf"
|
||||||
|
LOG_PREFIX = "zr"
|
||||||
|
LOG_FOLDER = "./log"
|
||||||
|
LOG_EXTENSION = ".log"
|
||||||
|
|
||||||
|
/* Configuration Folder Storage Path Constants */
|
||||||
|
CONF_HTTP_PROXY = "./conf/proxy"
|
||||||
|
CONF_STREAM_PROXY = "./conf/streamproxy"
|
||||||
|
CONF_CERT_STORE = "./conf/certs"
|
||||||
|
CONF_REDIRECTION = "./conf/redirect"
|
||||||
|
CONF_ACCESS_RULE = "./conf/access"
|
||||||
|
CONF_PATH_RULE = "./conf/rules/pathrules"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* System Startup Flags */
|
||||||
|
var (
|
||||||
|
webUIPort = flag.String("port", ":8000", "Management web interface listening port")
|
||||||
|
noauth = flag.Bool("noauth", false, "Disable authentication for management interface")
|
||||||
|
showver = flag.Bool("version", false, "Show version of this server")
|
||||||
|
allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)")
|
||||||
|
allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder")
|
||||||
|
mdnsName = flag.String("mdnsname", "", "mDNS name, leave empty to use default (zoraxy_{node-uuid}.local)")
|
||||||
|
ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
|
||||||
|
ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
||||||
|
runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode")
|
||||||
|
acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
|
||||||
|
acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)")
|
||||||
|
enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
||||||
|
staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
|
||||||
|
allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||||
|
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Global Variables and Handlers */
|
||||||
|
var (
|
||||||
|
nodeUUID = "generic" //System uuid in uuidv4 format, load from database on startup
|
||||||
|
bootTime = time.Now().Unix()
|
||||||
|
requireAuth = true //Require authentication for webmin panel, override from flag
|
||||||
|
|
||||||
|
/*
|
||||||
|
Binary Embedding File System
|
||||||
|
*/
|
||||||
|
//go:embed web/*
|
||||||
|
webres embed.FS
|
||||||
|
|
||||||
|
/*
|
||||||
|
Handler Modules
|
||||||
|
*/
|
||||||
|
sysdb *database.Database //System database
|
||||||
|
authAgent *auth.AuthAgent //Authentication agent
|
||||||
|
tlsCertManager *tlscert.Manager //TLS / SSL management
|
||||||
|
redirectTable *redirection.RuleTable //Handle special redirection rule sets
|
||||||
|
webminPanelMux *http.ServeMux //Server mux for handling webmin panel APIs
|
||||||
|
csrfMiddleware func(http.Handler) http.Handler //CSRF protection middleware
|
||||||
|
|
||||||
|
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
|
||||||
|
geodbStore *geodb.Store //GeoIP database, for resolving IP into country code
|
||||||
|
accessController *access.Controller //Access controller, handle black list and white list
|
||||||
|
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
|
||||||
|
statisticCollector *statistic.Collector //Collecting statistic from visitors
|
||||||
|
uptimeMonitor *uptime.Monitor //Uptime monitor service worker
|
||||||
|
mdnsScanner *mdns.MDNSHost //mDNS discovery services
|
||||||
|
ganManager *ganserv.NetworkManager //Global Area Network Manager
|
||||||
|
webSshManager *sshprox.Manager //Web SSH connection service
|
||||||
|
streamProxyManager *streamproxy.Manager //Stream Proxy Manager for TCP / UDP forwarding
|
||||||
|
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
||||||
|
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
||||||
|
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
||||||
|
forwardProxy *forwardproxy.Handler //HTTP Forward proxy, basically VPN for web browser
|
||||||
|
loadBalancer *loadbalance.RouteManager //Global scope loadbalancer, store the state of the lb routing
|
||||||
|
ssoHandler *sso.SSOHandler //Single Sign On handler
|
||||||
|
|
||||||
|
//Helper modules
|
||||||
|
EmailSender *email.Sender //Email sender that handle email sending
|
||||||
|
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
||||||
|
DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only
|
||||||
|
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
||||||
|
LogViewer *logviewer.Viewer //Log viewer HTTP handlers
|
||||||
|
)
|
134
src/main.go
134
src/main.go
@ -1,7 +1,36 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
______
|
||||||
|
|___ /
|
||||||
|
/ / ___ _ __ __ ___ ___ _
|
||||||
|
/ / / _ \| '__/ _` \ \/ / | | |
|
||||||
|
/ /_| (_) | | | (_| |> <| |_| |
|
||||||
|
/_____\___/|_| \__,_/_/\_\\__, |
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
|
||||||
|
Zoraxy - A general purpose HTTP reverse proxy and forwarding tool
|
||||||
|
Author: tobychui
|
||||||
|
License: AGPLv3
|
||||||
|
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, version 3 of the License or any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -13,100 +42,12 @@ import (
|
|||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
"imuslab.com/zoraxy/mod/access"
|
|
||||||
"imuslab.com/zoraxy/mod/acme"
|
|
||||||
"imuslab.com/zoraxy/mod/auth"
|
|
||||||
"imuslab.com/zoraxy/mod/auth/sso"
|
|
||||||
"imuslab.com/zoraxy/mod/database"
|
|
||||||
"imuslab.com/zoraxy/mod/dockerux"
|
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
|
||||||
"imuslab.com/zoraxy/mod/email"
|
|
||||||
"imuslab.com/zoraxy/mod/forwardproxy"
|
|
||||||
"imuslab.com/zoraxy/mod/ganserv"
|
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
|
||||||
"imuslab.com/zoraxy/mod/info/logviewer"
|
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
|
||||||
"imuslab.com/zoraxy/mod/pathrule"
|
|
||||||
"imuslab.com/zoraxy/mod/sshprox"
|
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
|
||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
|
||||||
"imuslab.com/zoraxy/mod/streamproxy"
|
|
||||||
"imuslab.com/zoraxy/mod/tlscert"
|
|
||||||
"imuslab.com/zoraxy/mod/update"
|
"imuslab.com/zoraxy/mod/update"
|
||||||
"imuslab.com/zoraxy/mod/uptime"
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
"imuslab.com/zoraxy/mod/webserv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// General flags
|
|
||||||
var webUIPort = flag.String("port", ":8000", "Management web interface listening port")
|
|
||||||
var noauth = flag.Bool("noauth", false, "Disable authentication for management interface")
|
|
||||||
var showver = flag.Bool("version", false, "Show version of this server")
|
|
||||||
var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)")
|
|
||||||
var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder")
|
|
||||||
var mdnsName = flag.String("mdnsname", "", "mDNS name, leave empty to use default (zoraxy_{node-uuid}.local)")
|
|
||||||
var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
|
|
||||||
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
|
||||||
var runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode")
|
|
||||||
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
|
|
||||||
var acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)")
|
|
||||||
var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
|
||||||
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
|
|
||||||
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
|
||||||
var enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
|
||||||
|
|
||||||
var (
|
/* SIGTERM handler, do shutdown sequences before closing */
|
||||||
name = "Zoraxy"
|
|
||||||
version = "3.1.3"
|
|
||||||
nodeUUID = "generic" //System uuid, in uuidv4 format
|
|
||||||
development = false //Set this to false to use embedded web fs
|
|
||||||
bootTime = time.Now().Unix()
|
|
||||||
|
|
||||||
/*
|
|
||||||
Binary Embedding File System
|
|
||||||
*/
|
|
||||||
//go:embed web/*
|
|
||||||
webres embed.FS
|
|
||||||
|
|
||||||
/*
|
|
||||||
Handler Modules
|
|
||||||
*/
|
|
||||||
sysdb *database.Database //System database
|
|
||||||
authAgent *auth.AuthAgent //Authentication agent
|
|
||||||
tlsCertManager *tlscert.Manager //TLS / SSL management
|
|
||||||
redirectTable *redirection.RuleTable //Handle special redirection rule sets
|
|
||||||
webminPanelMux *http.ServeMux //Server mux for handling webmin panel APIs
|
|
||||||
csrfMiddleware func(http.Handler) http.Handler //CSRF protection middleware
|
|
||||||
|
|
||||||
pathRuleHandler *pathrule.Handler //Handle specific path blocking or custom headers
|
|
||||||
geodbStore *geodb.Store //GeoIP database, for resolving IP into country code
|
|
||||||
accessController *access.Controller //Access controller, handle black list and white list
|
|
||||||
netstatBuffers *netstat.NetStatBuffers //Realtime graph buffers
|
|
||||||
statisticCollector *statistic.Collector //Collecting statistic from visitors
|
|
||||||
uptimeMonitor *uptime.Monitor //Uptime monitor service worker
|
|
||||||
mdnsScanner *mdns.MDNSHost //mDNS discovery services
|
|
||||||
ganManager *ganserv.NetworkManager //Global Area Network Manager
|
|
||||||
webSshManager *sshprox.Manager //Web SSH connection service
|
|
||||||
streamProxyManager *streamproxy.Manager //Stream Proxy Manager for TCP / UDP forwarding
|
|
||||||
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
|
||||||
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
|
||||||
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
|
||||||
forwardProxy *forwardproxy.Handler //HTTP Forward proxy, basically VPN for web browser
|
|
||||||
loadBalancer *loadbalance.RouteManager //Global scope loadbalancer, store the state of the lb routing
|
|
||||||
ssoHandler *sso.SSOHandler //Single Sign On handler
|
|
||||||
|
|
||||||
//Helper modules
|
|
||||||
EmailSender *email.Sender //Email sender that handle email sending
|
|
||||||
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
|
||||||
DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only
|
|
||||||
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
|
||||||
LogViewer *logviewer.Viewer
|
|
||||||
)
|
|
||||||
|
|
||||||
// Kill signal handler. Do something before the system the core terminate.
|
|
||||||
func SetupCloseHandler() {
|
func SetupCloseHandler() {
|
||||||
c := make(chan os.Signal, 2)
|
c := make(chan os.Signal, 2)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
@ -118,9 +59,7 @@ func SetupCloseHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ShutdownSeq() {
|
func ShutdownSeq() {
|
||||||
SystemWideLogger.Println("Shutting down " + name)
|
SystemWideLogger.Println("Shutting down " + SYSTEM_NAME)
|
||||||
//SystemWideLogger.Println("Closing GeoDB")
|
|
||||||
//geodbStore.Close()
|
|
||||||
SystemWideLogger.Println("Closing Netstats Listener")
|
SystemWideLogger.Println("Closing Netstats Listener")
|
||||||
netstatBuffers.Close()
|
netstatBuffers.Close()
|
||||||
SystemWideLogger.Println("Closing Statistic Collector")
|
SystemWideLogger.Println("Closing Statistic Collector")
|
||||||
@ -152,7 +91,7 @@ func main() {
|
|||||||
//Parse startup flags
|
//Parse startup flags
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *showver {
|
if *showver {
|
||||||
fmt.Println(name + " - Version " + version)
|
fmt.Println(SYSTEM_NAME + " - Version " + SYSTEM_VERSION)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +102,7 @@ func main() {
|
|||||||
|
|
||||||
if *enableAutoUpdate {
|
if *enableAutoUpdate {
|
||||||
fmt.Println("Checking required config update")
|
fmt.Println("Checking required config update")
|
||||||
update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(version))
|
update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(SYSTEM_VERSION))
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupCloseHandler()
|
SetupCloseHandler()
|
||||||
@ -185,7 +124,7 @@ func main() {
|
|||||||
webminPanelMux = http.NewServeMux()
|
webminPanelMux = http.NewServeMux()
|
||||||
csrfMiddleware = csrf.Protect(
|
csrfMiddleware = csrf.Protect(
|
||||||
[]byte(nodeUUID),
|
[]byte(nodeUUID),
|
||||||
csrf.CookieName("zoraxy-csrf"),
|
csrf.CookieName(CSRF_COOKIENAME),
|
||||||
csrf.Secure(false),
|
csrf.Secure(false),
|
||||||
csrf.Path("/"),
|
csrf.Path("/"),
|
||||||
csrf.SameSite(csrf.SameSiteLaxMode),
|
csrf.SameSite(csrf.SameSiteLaxMode),
|
||||||
@ -208,11 +147,10 @@ func main() {
|
|||||||
//Start the finalize sequences
|
//Start the finalize sequences
|
||||||
finalSequence()
|
finalSequence()
|
||||||
|
|
||||||
SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + *webUIPort)
|
SystemWideLogger.Println(SYSTEM_NAME + " started. Visit control panel at http://localhost" + *webUIPort)
|
||||||
err = http.ListenAndServe(*webUIPort, csrfMiddleware(webminPanelMux))
|
err = http.ListenAndServe(*webUIPort, csrfMiddleware(webminPanelMux))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,13 @@ func (a *ACMEHandler) Logf(message string, err error) {
|
|||||||
a.Logger.PrintAndLog("ACME", message, err)
|
a.Logger.PrintAndLog("ACME", message, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the ACMEHandler.
|
||||||
|
// ACME Handler does not need to close anything
|
||||||
|
// Function defined for future compatibility
|
||||||
|
func (a *ACMEHandler) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ObtainCert obtains a certificate for the specified domains.
|
// ObtainCert obtains a certificate for the specified domains.
|
||||||
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool, propagationTimeout int) (bool, error) {
|
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool, useDNS bool, propagationTimeout int) (bool, error) {
|
||||||
a.Logf("Obtaining certificate for: "+strings.Join(domains, ", "), nil)
|
a.Logf("Obtaining certificate for: "+strings.Join(domains, ", "), nil)
|
||||||
|
@ -308,7 +308,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
}
|
}
|
||||||
if CertExpireSoon(certBytes, a.EarlyRenewDays) || CertIsExpired(certBytes) {
|
if CertExpireSoon(certBytes, a.EarlyRenewDays) || CertIsExpired(certBytes) {
|
||||||
//This cert is expired
|
//This cert is expired
|
||||||
|
|
||||||
DNSName, err := ExtractDomains(certBytes)
|
DNSName, err := ExtractDomains(certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Maybe self signed. Ignore this
|
//Maybe self signed. Ignore this
|
||||||
@ -355,6 +354,7 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
return a.renewExpiredDomains(expiredCertList)
|
return a.renewExpiredDomains(expiredCertList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the auto renewer
|
||||||
func (a *AutoRenewer) Close() {
|
func (a *AutoRenewer) Close() {
|
||||||
if a.TickerstopChan != nil {
|
if a.TickerstopChan != nil {
|
||||||
a.TickerstopChan <- true
|
a.TickerstopChan <- true
|
||||||
@ -440,7 +440,7 @@ func (a *AutoRenewer) HanldeSetEAB(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle update auto renew DNS configuration
|
// Handle update auto renew DNS configuration
|
||||||
func (a *AutoRenewer) HanldeSetDNS(w http.ResponseWriter, r *http.Request) {
|
func (a *AutoRenewer) HandleSetDNS(w http.ResponseWriter, r *http.Request) {
|
||||||
dnsProvider, err := utils.PostPara(r, "dnsProvider")
|
dnsProvider, err := utils.PostPara(r, "dnsProvider")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "dnsProvider not set")
|
utils.SendErrorResponse(w, "dnsProvider not set")
|
||||||
|
@ -3,7 +3,7 @@ package acme
|
|||||||
/*
|
/*
|
||||||
CA.go
|
CA.go
|
||||||
|
|
||||||
This script load CA defination from embedded ca.json
|
This script load CA definition from embedded ca.json
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CA Defination, load from embeded json when startup
|
// CA definition, load from embeded json when startup
|
||||||
type CaDef struct {
|
type CaDef struct {
|
||||||
Production map[string]string
|
Production map[string]string
|
||||||
Test map[string]string
|
Test map[string]string
|
||||||
|
@ -210,8 +210,8 @@ func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
session.Values["authenticated"] = false
|
session.Values["authenticated"] = false
|
||||||
session.Values["username"] = nil
|
session.Values["username"] = nil
|
||||||
session.Save(r, w)
|
session.Options.MaxAge = -1
|
||||||
return nil
|
return session.Save(r, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current session username from request
|
// Get the current session username from request
|
||||||
@ -339,6 +339,7 @@ func (a *AuthAgent) CheckAuth(r *http.Request) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is authenticated
|
// Check if user is authenticated
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
return false
|
return false
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package dynamicproxy
|
package dynamicproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -16,7 +15,7 @@ func (h *ProxyHandler) handleAccessRouting(ruleID string, w http.ResponseWriter,
|
|||||||
accessRule, err := h.Parent.Option.AccessController.GetAccessRuleByID(ruleID)
|
accessRule, err := h.Parent.Option.AccessController.GetAccessRuleByID(ruleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Unable to load access rule. Target rule not found?
|
//Unable to load access rule. Target rule not found?
|
||||||
log.Println("[Proxy] Unable to load access rule: " + ruleID)
|
h.Parent.Option.Logger.PrintAndLog("proxy-access", "Unable to load access rule: "+ruleID, err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte("500 - Internal Server Error"))
|
w.Write([]byte("500 - Internal Server Error"))
|
||||||
return true
|
return true
|
||||||
|
@ -9,8 +9,15 @@ package domainsniff
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check if the domain is reachable and return err if not reachable
|
// Check if the domain is reachable and return err if not reachable
|
||||||
@ -25,7 +32,115 @@ func DomainReachableWithError(domain string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a domain have TLS but it is self-signed or expired
|
||||||
|
// Return false if sniff error
|
||||||
|
func DomainIsSelfSigned(domain string) bool {
|
||||||
|
//Extract the domain from URl in case the user input the full URL
|
||||||
|
host, port, err := net.SplitHostPort(domain)
|
||||||
|
if err != nil {
|
||||||
|
host = domain
|
||||||
|
} else {
|
||||||
|
domain = host + ":" + port
|
||||||
|
}
|
||||||
|
if !strings.Contains(domain, ":") {
|
||||||
|
domain = domain + ":443"
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the certificate
|
||||||
|
conn, err := net.Dial("tcp", domain)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
//Connect with TLS using secure verify
|
||||||
|
tlsConn := tls.Client(conn, nil)
|
||||||
|
err = tlsConn.Handshake()
|
||||||
|
if err == nil {
|
||||||
|
//This is a valid certificate
|
||||||
|
fmt.Println()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//Connect with TLS using insecure skip verify
|
||||||
|
config := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
tlsConn = tls.Client(conn, config)
|
||||||
|
err = tlsConn.Handshake()
|
||||||
|
//If the handshake is successful, this is a self-signed certificate
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if domain reachable
|
// Check if domain reachable
|
||||||
func DomainReachable(domain string) bool {
|
func DomainReachable(domain string) bool {
|
||||||
return DomainReachableWithError(domain) == nil
|
return DomainReachableWithError(domain) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if domain is served by a web server using HTTPS
|
||||||
|
func DomainUsesTLS(targetURL string) bool {
|
||||||
|
//Check if the site support https
|
||||||
|
httpsUrl := fmt.Sprintf("https://%s", targetURL)
|
||||||
|
httpUrl := fmt.Sprintf("http://%s", targetURL)
|
||||||
|
|
||||||
|
client := http.Client{Timeout: 5 * time.Second}
|
||||||
|
|
||||||
|
resp, err := client.Head(httpsUrl)
|
||||||
|
if err == nil && resp.StatusCode == http.StatusOK {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = client.Head(httpUrl)
|
||||||
|
if err == nil && resp.StatusCode == http.StatusOK {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the site is not reachable, return false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Request Handlers
|
||||||
|
*/
|
||||||
|
//Check if site support TLS
|
||||||
|
//Pass in ?selfsignchk=true to also check for self-signed certificate
|
||||||
|
func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
targetURL, err := utils.PostPara(r, "url")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid url given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the selfsign flag is set, also chec for self-signed certificate
|
||||||
|
_, err = utils.PostBool(r, "selfsignchk")
|
||||||
|
if err == nil {
|
||||||
|
//Return the https and selfsign status
|
||||||
|
type result struct {
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
SelfSign bool `json:"selfsign"`
|
||||||
|
}
|
||||||
|
|
||||||
|
scanResult := result{Protocol: "http", SelfSign: false}
|
||||||
|
|
||||||
|
if DomainUsesTLS(targetURL) {
|
||||||
|
scanResult.Protocol = "https"
|
||||||
|
if DomainIsSelfSigned(targetURL) {
|
||||||
|
scanResult.SelfSign = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(scanResult)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if DomainUsesTLS(targetURL) {
|
||||||
|
js, _ := json.Marshal("https")
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
js, _ := json.Marshal("http")
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -109,6 +109,8 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp
|
|||||||
thisTransporter.(*http.Transport).MaxConnsPerHost = optimalConcurrentConnection * 2
|
thisTransporter.(*http.Transport).MaxConnsPerHost = optimalConcurrentConnection * 2
|
||||||
thisTransporter.(*http.Transport).DisableCompression = true
|
thisTransporter.(*http.Transport).DisableCompression = true
|
||||||
|
|
||||||
|
//TODO: Add user adjustable timeout option here
|
||||||
|
|
||||||
if dpcOptions.IgnoreTLSVerification {
|
if dpcOptions.IgnoreTLSVerification {
|
||||||
//Ignore TLS certificate validation error
|
//Ignore TLS certificate validation error
|
||||||
thisTransporter.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
|
thisTransporter.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
|
||||||
|
@ -291,7 +291,7 @@ func (router *Router) Restart() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(800 * time.Millisecond)
|
||||||
// Start the server
|
// Start the server
|
||||||
err = router.StartProxyService()
|
err = router.StartProxyService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3,6 +3,7 @@ package geodb
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
"imuslab.com/zoraxy/mod/netutils"
|
"imuslab.com/zoraxy/mod/netutils"
|
||||||
@ -15,17 +16,22 @@ var geoipv4 []byte //Geodb dataset for ipv4
|
|||||||
var geoipv6 []byte //Geodb dataset for ipv6
|
var geoipv6 []byte //Geodb dataset for ipv6
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
geodb [][]string //Parsed geodb list
|
geodb [][]string //Parsed geodb list
|
||||||
geodbIpv6 [][]string //Parsed geodb list for ipv6
|
geodbIpv6 [][]string //Parsed geodb list for ipv6
|
||||||
geotrie *trie
|
geotrie *trie
|
||||||
geotrieIpv6 *trie
|
geotrieIpv6 *trie
|
||||||
sysdb *database.Database
|
sysdb *database.Database
|
||||||
option *StoreOptions
|
slowLookupCacheIpv4 map[string]string //Cache for slow lookup
|
||||||
|
slowLookupCacheIpv6 map[string]string //Cache for slow lookup
|
||||||
|
cacheClearTicker *time.Ticker //Ticker for clearing cache
|
||||||
|
cacheClearTickerStopChan chan bool //Stop channel for cache clear ticker
|
||||||
|
option *StoreOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoreOptions struct {
|
type StoreOptions struct {
|
||||||
AllowSlowIpv4LookUp bool
|
AllowSlowIpv4LookUp bool
|
||||||
AllowSloeIpv6Lookup bool
|
AllowSlowIpv6Lookup bool
|
||||||
|
SlowLookupCacheClearInterval time.Duration //Clear slow lookup cache interval
|
||||||
}
|
}
|
||||||
|
|
||||||
type CountryInfo struct {
|
type CountryInfo struct {
|
||||||
@ -50,18 +56,44 @@ func NewGeoDb(sysdb *database.Database, option *StoreOptions) (*Store, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ipv6Trie *trie
|
var ipv6Trie *trie
|
||||||
if !option.AllowSloeIpv6Lookup {
|
if !option.AllowSlowIpv6Lookup {
|
||||||
ipv6Trie = constrctTrieTree(parsedGeoDataIpv6)
|
ipv6Trie = constrctTrieTree(parsedGeoDataIpv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Store{
|
if option.SlowLookupCacheClearInterval == 0 {
|
||||||
geodb: parsedGeoData,
|
option.SlowLookupCacheClearInterval = 15 * time.Minute
|
||||||
geotrie: ipv4Trie,
|
}
|
||||||
geodbIpv6: parsedGeoDataIpv6,
|
|
||||||
geotrieIpv6: ipv6Trie,
|
//Create a new store
|
||||||
sysdb: sysdb,
|
thisGeoDBStore := &Store{
|
||||||
option: option,
|
geodb: parsedGeoData,
|
||||||
}, nil
|
geotrie: ipv4Trie,
|
||||||
|
geodbIpv6: parsedGeoDataIpv6,
|
||||||
|
geotrieIpv6: ipv6Trie,
|
||||||
|
sysdb: sysdb,
|
||||||
|
slowLookupCacheIpv4: make(map[string]string),
|
||||||
|
slowLookupCacheIpv6: make(map[string]string),
|
||||||
|
cacheClearTicker: time.NewTicker(option.SlowLookupCacheClearInterval),
|
||||||
|
cacheClearTickerStopChan: make(chan bool),
|
||||||
|
option: option,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Start cache clear ticker
|
||||||
|
if option.AllowSlowIpv4LookUp || option.AllowSlowIpv6Lookup {
|
||||||
|
go func(store *Store) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-store.cacheClearTickerStopChan:
|
||||||
|
return
|
||||||
|
case <-thisGeoDBStore.cacheClearTicker.C:
|
||||||
|
thisGeoDBStore.slowLookupCacheIpv4 = make(map[string]string)
|
||||||
|
thisGeoDBStore.slowLookupCacheIpv6 = make(map[string]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(thisGeoDBStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
return thisGeoDBStore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) {
|
func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) {
|
||||||
@ -73,8 +105,12 @@ func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the store
|
||||||
func (s *Store) Close() {
|
func (s *Store) Close() {
|
||||||
|
if s.option.AllowSlowIpv4LookUp || s.option.AllowSlowIpv6Lookup {
|
||||||
|
//Stop cache clear ticker
|
||||||
|
s.cacheClearTickerStopChan <- true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
|
func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
|
||||||
|
@ -44,6 +44,7 @@ func TestResolveCountryCodeFromIP(t *testing.T) {
|
|||||||
store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{
|
store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
0,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error creating store: %v", err)
|
t.Errorf("error creating store: %v", err)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,12 @@ func (s *Store) slowSearchIpv4(ipAddr string) string {
|
|||||||
if isReservedIP(ipAddr) {
|
if isReservedIP(ipAddr) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if already in cache
|
||||||
|
if cc, ok := s.slowLookupCacheIpv4[ipAddr]; ok {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
for _, ipRange := range s.geodb {
|
for _, ipRange := range s.geodb {
|
||||||
startIp := ipRange[0]
|
startIp := ipRange[0]
|
||||||
endIp := ipRange[1]
|
endIp := ipRange[1]
|
||||||
@ -63,6 +69,8 @@ func (s *Store) slowSearchIpv4(ipAddr string) string {
|
|||||||
|
|
||||||
inRange, _ := isIPv4InRange(startIp, endIp, ipAddr)
|
inRange, _ := isIPv4InRange(startIp, endIp, ipAddr)
|
||||||
if inRange {
|
if inRange {
|
||||||
|
//Add to cache
|
||||||
|
s.slowLookupCacheIpv4[ipAddr] = cc
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +81,12 @@ func (s *Store) slowSearchIpv6(ipAddr string) string {
|
|||||||
if isReservedIP(ipAddr) {
|
if isReservedIP(ipAddr) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if already in cache
|
||||||
|
if cc, ok := s.slowLookupCacheIpv6[ipAddr]; ok {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
|
||||||
for _, ipRange := range s.geodbIpv6 {
|
for _, ipRange := range s.geodbIpv6 {
|
||||||
startIp := ipRange[0]
|
startIp := ipRange[0]
|
||||||
endIp := ipRange[1]
|
endIp := ipRange[1]
|
||||||
@ -80,6 +94,8 @@ func (s *Store) slowSearchIpv6(ipAddr string) string {
|
|||||||
|
|
||||||
inRange, _ := isIPv6InRange(startIp, endIp, ipAddr)
|
inRange, _ := isIPv6InRange(startIp, endIp, ipAddr)
|
||||||
if inRange {
|
if inRange {
|
||||||
|
//Add to cache
|
||||||
|
s.slowLookupCacheIpv6[ipAddr] = cc
|
||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,9 +169,16 @@ func (n *NetStatBuffers) HandleGetBufferedNetworkInterfaceStats(w http.ResponseW
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetStatBuffers) Close() {
|
func (n *NetStatBuffers) Close() {
|
||||||
n.StopChan <- true
|
//Fixed issue #394 for stopping netstat listener on platforms not supported platforms
|
||||||
time.Sleep(300 * time.Millisecond)
|
if n.StopChan != nil {
|
||||||
n.EventTicker.Stop()
|
n.StopChan <- true
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.EventTicker != nil {
|
||||||
|
n.EventTicker.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NetStatBuffers) HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
|
func (n *NetStatBuffers) HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -270,11 +277,11 @@ func (n *NetStatBuffers) GetNetworkInterfaceStats() (int64, int64, error) {
|
|||||||
allIfaceRxByteFiles, err := filepath.Glob("/sys/class/net/*/statistics/rx_bytes")
|
allIfaceRxByteFiles, err := filepath.Glob("/sys/class/net/*/statistics/rx_bytes")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Permission denied
|
//Permission denied
|
||||||
return 0, 0, errors.New("Access denied")
|
return 0, 0, errors.New("access denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allIfaceRxByteFiles) == 0 {
|
if len(allIfaceRxByteFiles) == 0 {
|
||||||
return 0, 0, errors.New("No valid iface found")
|
return 0, 0, errors.New("no valid iface found")
|
||||||
}
|
}
|
||||||
|
|
||||||
rxSum := int64(0)
|
rxSum := int64(0)
|
||||||
@ -334,5 +341,5 @@ func (n *NetStatBuffers) GetNetworkInterfaceStats() (int64, int64, error) {
|
|||||||
return 0, 0, nil //no ethernet adapters with en*/<Link#*>
|
return 0, 0, nil //no ethernet adapters with en*/<Link#*>
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, 0, errors.New("Platform not supported")
|
return 0, 0, errors.New("platform not supported")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package v308
|
package v308
|
||||||
|
|
||||||
/*
|
/*
|
||||||
v307 type definations
|
v307 type definitions
|
||||||
|
|
||||||
This file wrap up the self-contained data structure
|
This file wrap up the self-contained data structure
|
||||||
for v3.0.7 structure and allow automatic updates
|
for v3.0.7 structure and allow automatic updates
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package v308
|
package v308
|
||||||
|
|
||||||
/*
|
/*
|
||||||
v308 type definations
|
v308 type definition
|
||||||
|
|
||||||
This file wrap up the self-contained data structure
|
This file wrap up the self-contained data structure
|
||||||
for v3.0.8 structure and allow automatic updates
|
for v3.0.8 structure and allow automatic updates
|
||||||
|
@ -27,18 +27,18 @@ func ReverseProxtInit() {
|
|||||||
/*
|
/*
|
||||||
Load Reverse Proxy Global Settings
|
Load Reverse Proxy Global Settings
|
||||||
*/
|
*/
|
||||||
inboundPort := 80
|
inboundPort := 443
|
||||||
if sysdb.KeyExists("settings", "inbound") {
|
if sysdb.KeyExists("settings", "inbound") {
|
||||||
sysdb.Read("settings", "inbound", &inboundPort)
|
sysdb.Read("settings", "inbound", &inboundPort)
|
||||||
SystemWideLogger.Println("Serving inbound port ", inboundPort)
|
SystemWideLogger.Println("Serving inbound port ", inboundPort)
|
||||||
} else {
|
} else {
|
||||||
SystemWideLogger.Println("Inbound port not set. Using default (80)")
|
SystemWideLogger.Println("Inbound port not set. Using default (443)")
|
||||||
}
|
}
|
||||||
|
|
||||||
useTls := false
|
useTls := true
|
||||||
sysdb.Read("settings", "usetls", &useTls)
|
sysdb.Read("settings", "usetls", &useTls)
|
||||||
if useTls {
|
if useTls {
|
||||||
SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS")
|
SystemWideLogger.Println("TLS mode enabled. Serving proxy request with TLS")
|
||||||
} else {
|
} else {
|
||||||
SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
|
SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ func ReverseProxtInit() {
|
|||||||
SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy")
|
SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy")
|
||||||
}
|
}
|
||||||
|
|
||||||
listenOnPort80 := false
|
listenOnPort80 := true
|
||||||
sysdb.Read("settings", "listenP80", &listenOnPort80)
|
sysdb.Read("settings", "listenP80", &listenOnPort80)
|
||||||
if listenOnPort80 {
|
if listenOnPort80 {
|
||||||
SystemWideLogger.Println("Port 80 listener enabled")
|
SystemWideLogger.Println("Port 80 listener enabled")
|
||||||
@ -67,7 +67,7 @@ func ReverseProxtInit() {
|
|||||||
SystemWideLogger.Println("Port 80 listener disabled")
|
SystemWideLogger.Println("Port 80 listener disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
forceHttpsRedirect := false
|
forceHttpsRedirect := true
|
||||||
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
||||||
if forceHttpsRedirect {
|
if forceHttpsRedirect {
|
||||||
SystemWideLogger.Println("Force HTTPS mode enabled")
|
SystemWideLogger.Println("Force HTTPS mode enabled")
|
||||||
@ -85,7 +85,7 @@ func ReverseProxtInit() {
|
|||||||
|
|
||||||
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
||||||
HostUUID: nodeUUID,
|
HostUUID: nodeUUID,
|
||||||
HostVersion: version,
|
HostVersion: SYSTEM_VERSION,
|
||||||
Port: inboundPort,
|
Port: inboundPort,
|
||||||
UseTls: useTls,
|
UseTls: useTls,
|
||||||
ForceTLSLatest: forceLatestTLSVersion,
|
ForceTLSLatest: forceLatestTLSVersion,
|
||||||
@ -1085,6 +1085,7 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
if dynamicProxyRouter.Running {
|
if dynamicProxyRouter.Running {
|
||||||
dynamicProxyRouter.StopProxyService()
|
dynamicProxyRouter.StopProxyService()
|
||||||
dynamicProxyRouter.Option.Port = newIncomingPortInt
|
dynamicProxyRouter.Option.Port = newIncomingPortInt
|
||||||
|
time.Sleep(1 * time.Second) //Fixed start fail issue
|
||||||
dynamicProxyRouter.StartProxyService()
|
dynamicProxyRouter.StartProxyService()
|
||||||
} else {
|
} else {
|
||||||
//Only change setting but not starting the proxy service
|
//Only change setting but not starting the proxy service
|
||||||
@ -1173,7 +1174,7 @@ func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a Custom Header Defination type
|
//Create a Custom Header Definition type
|
||||||
var rewriteDirection rewrite.HeaderDirection
|
var rewriteDirection rewrite.HeaderDirection
|
||||||
if direction == "toOrigin" {
|
if direction == "toOrigin" {
|
||||||
rewriteDirection = rewrite.HeaderDirection_ZoraxyToUpstream
|
rewriteDirection = rewrite.HeaderDirection_ZoraxyToUpstream
|
||||||
|
@ -27,7 +27,7 @@ func FSHandler(handler http.Handler) http.Handler {
|
|||||||
Development Mode Override
|
Development Mode Override
|
||||||
=> Web root is located in /
|
=> Web root is located in /
|
||||||
*/
|
*/
|
||||||
if development && strings.HasPrefix(r.URL.Path, "/web/") {
|
if DEVELOPMENT_BUILD && strings.HasPrefix(r.URL.Path, "/web/") {
|
||||||
u, _ := url.Parse(strings.TrimPrefix(r.URL.Path, "/web"))
|
u, _ := url.Parse(strings.TrimPrefix(r.URL.Path, "/web"))
|
||||||
r.URL = u
|
r.URL = u
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ func FSHandler(handler http.Handler) http.Handler {
|
|||||||
Production Mode Override
|
Production Mode Override
|
||||||
=> Web root is located in /web
|
=> Web root is located in /web
|
||||||
*/
|
*/
|
||||||
if !development && r.URL.Path == "/" {
|
if !DEVELOPMENT_BUILD && r.URL.Path == "/" {
|
||||||
//Redirect to web UI
|
//Redirect to web UI
|
||||||
http.Redirect(w, r, "/web/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/web/", http.StatusTemporaryRedirect)
|
||||||
return
|
return
|
||||||
@ -93,7 +93,7 @@ func FSHandler(handler http.Handler) http.Handler {
|
|||||||
|
|
||||||
// Production path fix wrapper. Fix the path on production or development environment
|
// Production path fix wrapper. Fix the path on production or development environment
|
||||||
func ppf(relativeFilepath string) string {
|
func ppf(relativeFilepath string) string {
|
||||||
if !development {
|
if !DEVELOPMENT_BUILD {
|
||||||
return strings.ReplaceAll(filepath.Join("/web/", relativeFilepath), "\\", "/")
|
return strings.ReplaceAll(filepath.Join("/web/", relativeFilepath), "\\", "/")
|
||||||
}
|
}
|
||||||
return relativeFilepath
|
return relativeFilepath
|
||||||
@ -111,7 +111,7 @@ func handleInjectHTML(w http.ResponseWriter, r *http.Request, relativeFilepath s
|
|||||||
if len(relativeFilepath) > 0 && relativeFilepath[len(relativeFilepath)-1:] == "/" {
|
if len(relativeFilepath) > 0 && relativeFilepath[len(relativeFilepath)-1:] == "/" {
|
||||||
relativeFilepath = relativeFilepath + "index.html"
|
relativeFilepath = relativeFilepath + "index.html"
|
||||||
}
|
}
|
||||||
if development {
|
if DEVELOPMENT_BUILD {
|
||||||
//Load from disk
|
//Load from disk
|
||||||
targetFilePath := strings.ReplaceAll(filepath.Join("web/", relativeFilepath), "\\", "/")
|
targetFilePath := strings.ReplaceAll(filepath.Join("web/", relativeFilepath), "\\", "/")
|
||||||
content, err = os.ReadFile(targetFilePath)
|
content, err = os.ReadFile(targetFilePath)
|
||||||
|
51
src/start.go
51
src/start.go
@ -52,19 +52,19 @@ var (
|
|||||||
|
|
||||||
func startupSequence() {
|
func startupSequence() {
|
||||||
//Start a system wide logger and log viewer
|
//Start a system wide logger and log viewer
|
||||||
l, err := logger.NewLogger("zr", "./log")
|
l, err := logger.NewLogger(LOG_PREFIX, LOG_FOLDER)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
SystemWideLogger = l
|
SystemWideLogger = l
|
||||||
} else {
|
} else {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
LogViewer = logviewer.NewLogViewer(&logviewer.ViewerOption{
|
LogViewer = logviewer.NewLogViewer(&logviewer.ViewerOption{
|
||||||
RootFolder: "./log",
|
RootFolder: LOG_FOLDER,
|
||||||
Extension: ".log",
|
Extension: LOG_EXTENSION,
|
||||||
})
|
})
|
||||||
|
|
||||||
//Create database
|
//Create database
|
||||||
db, err := database.NewDatabase("sys.db", false)
|
db, err := database.NewDatabase(DATABASE_PATH, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -73,21 +73,21 @@ func startupSequence() {
|
|||||||
sysdb.NewTable("settings")
|
sysdb.NewTable("settings")
|
||||||
|
|
||||||
//Create tmp folder and conf folder
|
//Create tmp folder and conf folder
|
||||||
os.MkdirAll("./tmp", 0775)
|
os.MkdirAll(TMP_FOLDER, 0775)
|
||||||
os.MkdirAll("./conf/proxy/", 0775)
|
os.MkdirAll(CONF_HTTP_PROXY, 0775)
|
||||||
|
|
||||||
//Create an auth agent
|
//Create an auth agent
|
||||||
sessionKey, err := auth.GetSessionKey(sysdb, SystemWideLogger)
|
sessionKey, err := auth.GetSessionKey(sysdb, SystemWideLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
authAgent = auth.NewAuthenticationAgent(name, []byte(sessionKey), sysdb, true, SystemWideLogger, func(w http.ResponseWriter, r *http.Request) {
|
authAgent = auth.NewAuthenticationAgent(SYSTEM_NAME, []byte(sessionKey), sysdb, true, SystemWideLogger, func(w http.ResponseWriter, r *http.Request) {
|
||||||
//Not logged in. Redirecting to login page
|
//Not logged in. Redirecting to login page
|
||||||
http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect)
|
http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
|
||||||
//Create a TLS certificate manager
|
//Create a TLS certificate manager
|
||||||
tlsCertManager, err = tlscert.NewManager("./conf/certs", development, SystemWideLogger)
|
tlsCertManager, err = tlscert.NewManager(CONF_CERT_STORE, DEVELOPMENT_BUILD, SystemWideLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -96,15 +96,16 @@ func startupSequence() {
|
|||||||
db.NewTable("redirect")
|
db.NewTable("redirect")
|
||||||
redirectAllowRegexp := false
|
redirectAllowRegexp := false
|
||||||
db.Read("redirect", "regex", &redirectAllowRegexp)
|
db.Read("redirect", "regex", &redirectAllowRegexp)
|
||||||
redirectTable, err = redirection.NewRuleTable("./conf/redirect", redirectAllowRegexp, SystemWideLogger)
|
redirectTable, err = redirection.NewRuleTable(CONF_REDIRECTION, redirectAllowRegexp, SystemWideLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a geodb store
|
//Create a geodb store
|
||||||
geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
|
geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
|
||||||
AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup,
|
AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup,
|
||||||
AllowSloeIpv6Lookup: !*enableHighSpeedGeoIPLookup,
|
AllowSlowIpv6Lookup: !*enableHighSpeedGeoIPLookup,
|
||||||
|
SlowLookupCacheClearInterval: GEODB_CACHE_CLEAR_INTERVAL * time.Minute,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -121,7 +122,7 @@ func startupSequence() {
|
|||||||
accessController, err = access.NewAccessController(&access.Options{
|
accessController, err = access.NewAccessController(&access.Options{
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
GeoDB: geodbStore,
|
GeoDB: geodbStore,
|
||||||
ConfigFolder: "./conf/access",
|
ConfigFolder: CONF_ACCESS_RULE,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -154,7 +155,7 @@ func startupSequence() {
|
|||||||
//Start the static web server
|
//Start the static web server
|
||||||
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
||||||
Sysdb: sysdb,
|
Sysdb: sysdb,
|
||||||
Port: "5487", //Default Port
|
Port: strconv.Itoa(WEBSERV_DEFAULT_PORT), //Default Port
|
||||||
WebRoot: *staticWebServerRoot,
|
WebRoot: *staticWebServerRoot,
|
||||||
EnableDirectoryListing: true,
|
EnableDirectoryListing: true,
|
||||||
EnableWebDirManager: *allowWebFileManager,
|
EnableWebDirManager: *allowWebFileManager,
|
||||||
@ -179,7 +180,7 @@ func startupSequence() {
|
|||||||
|
|
||||||
pathRuleHandler = pathrule.NewPathRuleHandler(&pathrule.Options{
|
pathRuleHandler = pathrule.NewPathRuleHandler(&pathrule.Options{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
ConfigFolder: "./conf/rules/pathrules",
|
ConfigFolder: CONF_PATH_RULE,
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -197,7 +198,7 @@ func startupSequence() {
|
|||||||
|
|
||||||
hostName := *mdnsName
|
hostName := *mdnsName
|
||||||
if hostName == "" {
|
if hostName == "" {
|
||||||
hostName = "zoraxy_" + nodeUUID
|
hostName = MDNS_HOSTNAME_PREFIX + nodeUUID
|
||||||
} else {
|
} else {
|
||||||
//Trim off the suffix
|
//Trim off the suffix
|
||||||
hostName = strings.TrimSuffix(hostName, ".local")
|
hostName = strings.TrimSuffix(hostName, ".local")
|
||||||
@ -206,24 +207,24 @@ func startupSequence() {
|
|||||||
mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{
|
mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{
|
||||||
HostName: hostName,
|
HostName: hostName,
|
||||||
Port: portInt,
|
Port: portInt,
|
||||||
Domain: "zoraxy.aroz.org",
|
Domain: MDNS_IDENTIFY_DOMAIN,
|
||||||
Model: "Network Gateway",
|
Model: MDNS_IDENTIFY_DEVICE_TYPE,
|
||||||
UUID: nodeUUID,
|
UUID: nodeUUID,
|
||||||
Vendor: "imuslab.com",
|
Vendor: MDNS_IDENTIFY_VENDOR,
|
||||||
BuildVersion: version,
|
BuildVersion: SYSTEM_VERSION,
|
||||||
}, "")
|
}, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services")
|
SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services")
|
||||||
} else {
|
} else {
|
||||||
//Start initial scanning
|
//Start initial scanning
|
||||||
go func() {
|
go func() {
|
||||||
hosts := mdnsScanner.Scan(30, "")
|
hosts := mdnsScanner.Scan(MDNS_SCAN_TIMEOUT, "")
|
||||||
previousmdnsScanResults = hosts
|
previousmdnsScanResults = hosts
|
||||||
SystemWideLogger.Println("mDNS Startup scan completed")
|
SystemWideLogger.Println("mDNS Startup scan completed")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//Create a ticker to update mDNS results every 5 minutes
|
//Create a ticker to update mDNS results every 5 minutes
|
||||||
ticker := time.NewTicker(15 * time.Minute)
|
ticker := time.NewTicker(MDNS_SCAN_UPDATE_INTERVAL * time.Minute)
|
||||||
stopChan := make(chan bool)
|
stopChan := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -231,7 +232,7 @@ func startupSequence() {
|
|||||||
case <-stopChan:
|
case <-stopChan:
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
hosts := mdnsScanner.Scan(30, "")
|
hosts := mdnsScanner.Scan(MDNS_SCAN_TIMEOUT, "")
|
||||||
previousmdnsScanResults = hosts
|
previousmdnsScanResults = hosts
|
||||||
SystemWideLogger.Println("mDNS scan result updated")
|
SystemWideLogger.Println("mDNS scan result updated")
|
||||||
}
|
}
|
||||||
@ -265,7 +266,7 @@ func startupSequence() {
|
|||||||
//Create TCP Proxy Manager
|
//Create TCP Proxy Manager
|
||||||
streamProxyManager, err = streamproxy.NewStreamProxy(&streamproxy.Options{
|
streamProxyManager, err = streamproxy.NewStreamProxy(&streamproxy.Options{
|
||||||
AccessControlHandler: accessController.DefaultAccessRule.AllowConnectionAccess,
|
AccessControlHandler: accessController.DefaultAccessRule.AllowConnectionAccess,
|
||||||
ConfigStore: "./conf/streamproxy",
|
ConfigStore: CONF_STREAM_PROXY,
|
||||||
Logger: SystemWideLogger,
|
Logger: SystemWideLogger,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -303,8 +304,8 @@ func startupSequence() {
|
|||||||
sysdb.NewTable("acmepref")
|
sysdb.NewTable("acmepref")
|
||||||
acmeHandler = initACME()
|
acmeHandler = initACME()
|
||||||
acmeAutoRenewer, err = acme.NewAutoRenewer(
|
acmeAutoRenewer, err = acme.NewAutoRenewer(
|
||||||
"./conf/acme_conf.json",
|
ACME_AUTORENEW_CONFIG_PATH,
|
||||||
"./conf/certs/",
|
CONF_CERT_STORE,
|
||||||
int64(*acmeAutoRenewInterval),
|
int64(*acmeAutoRenewInterval),
|
||||||
*acmeCertAutoRenewDays,
|
*acmeCertAutoRenewDays,
|
||||||
acmeHandler,
|
acmeHandler,
|
||||||
|
@ -841,6 +841,25 @@
|
|||||||
function initBannedCountryList(){
|
function initBannedCountryList(){
|
||||||
$.get("/api/blacklist/list?type=country&id=" + currentEditingAccessRule, function(data) {
|
$.get("/api/blacklist/list?type=country&id=" + currentEditingAccessRule, function(data) {
|
||||||
let bannedListHtml = '';
|
let bannedListHtml = '';
|
||||||
|
|
||||||
|
//Check if the country code list contains all eu countries. If yes, replace it with "EU"
|
||||||
|
let allEu = true;
|
||||||
|
let euCountries = getEUCCs();
|
||||||
|
for (var i = 0; i < euCountries.length; i++){
|
||||||
|
if (!data.includes(euCountries[i])){
|
||||||
|
allEu = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allEu){
|
||||||
|
//Remove EU countries from the list and replace it with EU
|
||||||
|
data = data.filter(function(value, index, arr){
|
||||||
|
return !euCountries.includes(value);
|
||||||
|
});
|
||||||
|
data.push("eu");
|
||||||
|
}
|
||||||
|
|
||||||
data.forEach((countryCode) => {
|
data.forEach((countryCode) => {
|
||||||
bannedListHtml += `
|
bannedListHtml += `
|
||||||
<tr>
|
<tr>
|
||||||
@ -919,18 +938,48 @@
|
|||||||
//Whitelist country table
|
//Whitelist country table
|
||||||
function initWhitelistCountryList(){
|
function initWhitelistCountryList(){
|
||||||
$.get("/api/whitelist/list?type=country&id=" + currentEditingAccessRule, function(data) {
|
$.get("/api/whitelist/list?type=country&id=" + currentEditingAccessRule, function(data) {
|
||||||
let bannedListHtml = '';
|
let whiteListHTML = '';
|
||||||
|
|
||||||
|
//Check if the country code list contains all eu countries. If yes, replace it with "EU"
|
||||||
|
let allEu = true;
|
||||||
|
let euCountries = getEUCCs();
|
||||||
|
let countryCodesIndata = data.map(function(item){
|
||||||
|
//data[n].CC is the country code
|
||||||
|
return item.CC;
|
||||||
|
});
|
||||||
|
for (var i = 0; i < euCountries.length; i++){
|
||||||
|
if (!countryCodesIndata.includes(euCountries[i])){
|
||||||
|
allEu = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allEu){
|
||||||
|
//Remove EU countries from the list and replace it with EU
|
||||||
|
data = data.filter(function(value, index, arr){
|
||||||
|
return !euCountries.includes(value.CC);
|
||||||
|
});
|
||||||
|
data.push({
|
||||||
|
CC: "eu"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
data.forEach((countryWhitelistEntry) => {
|
data.forEach((countryWhitelistEntry) => {
|
||||||
let countryCode = countryWhitelistEntry.CC;
|
let countryCode = countryWhitelistEntry.CC;
|
||||||
bannedListHtml += `
|
whiteListHTML += `
|
||||||
<tr>
|
<tr>
|
||||||
<td><i class="${countryCode} flag"></i> ${getCountryName(countryCode)} (${countryCode.toUpperCase()})</td>
|
<td><i class="${countryCode} flag"></i> ${getCountryName(countryCode)} (${countryCode.toUpperCase()})</td>
|
||||||
<td><button class="ui red basic mini icon button" onclick="removeFromWhiteList('${countryCode}')"><i class="trash icon"></i></button></td>
|
<td><button class="ui red basic mini icon button" onclick="removeFromWhiteList('${countryCode}')"><i class="trash icon"></i></button></td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
$('#whitelistCountryList').html(bannedListHtml);
|
$('#whitelistCountryList').html(whiteListHTML);
|
||||||
filterCountries(data, "#countrySelectorWhitelist .menu .item");
|
|
||||||
|
//Map the data.CC to the country code
|
||||||
|
let countryCodes = data.map(function(item){
|
||||||
|
return item.CC;
|
||||||
|
});
|
||||||
|
filterCountries(countryCodes, "#countrySelectorWhitelist .menu .item");
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
$('#whitelistCountryList').append(`
|
$('#whitelistCountryList').append(`
|
||||||
<tr>
|
<tr>
|
||||||
@ -1016,6 +1065,10 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEUCCs(){
|
||||||
|
return ["at","be","bg","cy","cz","de","dk","ee","es","fi","fr","gr","hr","hu","ie","it","lt","lu","lv","mt","nl","pl","pt","se","si","sk"];
|
||||||
|
}
|
||||||
|
|
||||||
function addCountryToBlacklist() {
|
function addCountryToBlacklist() {
|
||||||
var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
|
var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
|
||||||
let ccs = [countryCode];
|
let ccs = [countryCode];
|
||||||
@ -1025,48 +1078,50 @@
|
|||||||
ccs = countryCode.split(",");
|
ccs = countryCode.split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
let counter = 0;
|
//If the ccs includes "eu", remove the "eu" and add all eu country code to the list
|
||||||
for(var i = 0; i < ccs.length; i++){
|
if (ccs.includes("eu")){
|
||||||
let thisCountryCode = ccs[i];
|
ccs = ccs.concat(getEUCCs());
|
||||||
$.cjax({
|
ccs = ccs.filter(function(item){
|
||||||
type: "POST",
|
return item != "eu";
|
||||||
url: "/api/blacklist/country/add",
|
|
||||||
method: "POST",
|
|
||||||
data: { cc: thisCountryCode, id: currentEditingAccessRule},
|
|
||||||
success: function(response) {
|
|
||||||
if (response.error != undefined){
|
|
||||||
msgbox(response.error, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter == (ccs.length - 1)){
|
|
||||||
//Last item
|
|
||||||
setTimeout(function(){
|
|
||||||
initBannedCountryList();
|
|
||||||
if (ccs.length == 1){
|
|
||||||
//Single country
|
|
||||||
msgbox(`Added ${getCountryName(ccs[0])} to blacklist`);
|
|
||||||
}else{
|
|
||||||
msgbox(ccs.length + " countries added to blacklist");
|
|
||||||
}
|
|
||||||
|
|
||||||
}, (ccs.length==1)?0:100);
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
// handle error response
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let counter = ccs.length;
|
||||||
|
$.cjax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/api/blacklist/country/add",
|
||||||
|
method: "POST",
|
||||||
|
data: { cc: ccs.join(","), id: currentEditingAccessRule},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.error != undefined){
|
||||||
|
msgbox(response.error, false);
|
||||||
|
}
|
||||||
|
initBannedCountryList();
|
||||||
|
if (ccs.length == 1){
|
||||||
|
//Single country
|
||||||
|
msgbox(`Added ${getCountryName(ccs[0])} to blacklist`);
|
||||||
|
}else{
|
||||||
|
msgbox(ccs.length + " countries added to blacklist");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
// handle error response
|
||||||
|
}
|
||||||
|
});
|
||||||
$('#countrySelector').dropdown('clear');
|
$('#countrySelector').dropdown('clear');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFromBannedList(countryCode){
|
function removeFromBannedList(countryCode){
|
||||||
countryCode = countryCode.toLowerCase();
|
|
||||||
let countryName = getCountryName(countryCode);
|
let countryName = getCountryName(countryCode);
|
||||||
|
if (countryCode == "eu"){
|
||||||
|
let euCountries = getEUCCs();
|
||||||
|
countryCode = euCountries.join(",");
|
||||||
|
countryName = "European Union";
|
||||||
|
}else{
|
||||||
|
countryCode = countryCode.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/blacklist/country/remove",
|
url: "/api/blacklist/country/remove",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1162,44 +1217,53 @@
|
|||||||
//Usually just a few countries a for loop will get the job done
|
//Usually just a few countries a for loop will get the job done
|
||||||
ccs = countryCode.split(",");
|
ccs = countryCode.split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
let counter = 0;
|
|
||||||
for(var i = 0; i < ccs.length; i++){
|
|
||||||
let thisCountryCode = ccs[i];
|
|
||||||
$.cjax({
|
|
||||||
type: "POST",
|
|
||||||
url: "/api/whitelist/country/add",
|
|
||||||
data: { cc: thisCountryCode , id: currentEditingAccessRule},
|
|
||||||
success: function(response) {
|
|
||||||
if (response.error != undefined){
|
|
||||||
msgbox(response.error, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter == (ccs.length - 1)){
|
//If the ccs includes "eu", remove the "eu" and add all eu country code to the list
|
||||||
setTimeout(function(){
|
if (ccs.includes("eu")){
|
||||||
initWhitelistCountryList();
|
ccs = ccs.filter(function(item){
|
||||||
if (ccs.length == 1){
|
return item != "eu";
|
||||||
//Single country
|
|
||||||
msgbox(`Added ${getCountryName(ccs[0])} to whitelist`);
|
|
||||||
}else{
|
|
||||||
msgbox(ccs.length + " countries added to whitelist");
|
|
||||||
}
|
|
||||||
}, (ccs.length==1)?0:100);
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
// handle error response
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
ccs = ccs.concat(getEUCCs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let counter = ccs.length;
|
||||||
|
$.cjax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/api/whitelist/country/add",
|
||||||
|
data: { cc: ccs.join(",") , id: currentEditingAccessRule},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.error != undefined){
|
||||||
|
msgbox(response.error, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
initWhitelistCountryList();
|
||||||
|
if (ccs.length == 1){
|
||||||
|
//Single country
|
||||||
|
msgbox(`Added ${getCountryName(ccs[0])} to whitelist`);
|
||||||
|
}else{
|
||||||
|
msgbox(ccs.length + " countries added to whitelist");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
// handle error response
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$('#countrySelectorWhitelist').dropdown('clear');
|
$('#countrySelectorWhitelist').dropdown('clear');
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFromWhiteList(countryCode){
|
//Remove from whitelist, accepts a country code or "eu" for all EU countries
|
||||||
if (confirm("Confirm removing " + getCountryName(countryCode) + " from whitelist?")){
|
function removeFromWhiteList(countryCode, skipConfirm = true){
|
||||||
|
let countryName = getCountryName(countryCode);
|
||||||
|
if (countryCode == "eu"){
|
||||||
|
let euCountries = getEUCCs();
|
||||||
|
countryCode = euCountries.join(",");
|
||||||
|
countryName = "European Union";
|
||||||
|
}else{
|
||||||
countryCode = countryCode.toLowerCase();
|
countryCode = countryCode.toLowerCase();
|
||||||
|
}
|
||||||
|
if (skipConfirm || confirm("Confirm removing " + getCountryName(countryCode) + " from whitelist?")){
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/whitelist/country/remove",
|
url: "/api/whitelist/country/remove",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1208,6 +1272,7 @@
|
|||||||
if (response.error != undefined){
|
if (response.error != undefined){
|
||||||
msgbox(response.error, false);
|
msgbox(response.error, false);
|
||||||
}
|
}
|
||||||
|
msgbox(countryName + " removed from whitelist");
|
||||||
initWhitelistCountryList();
|
initWhitelistCountryList();
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
@ -1276,19 +1341,27 @@
|
|||||||
/*
|
/*
|
||||||
Common Utilities
|
Common Utilities
|
||||||
*/
|
*/
|
||||||
function filterCountries(codesToShow, selector="#countrySelector .menu .item") {
|
function filterCountries(alreadySelectedCCs, selector="#countrySelector .menu .item") {
|
||||||
// get all items in the dropdown
|
// get all items in the dropdown
|
||||||
const items = document.querySelectorAll(selector);
|
const items = document.querySelectorAll(selector);
|
||||||
|
const euCountries = getEUCCs();
|
||||||
|
//Replce "eu" in alreadySelectedCCs with all EU countries
|
||||||
|
if (alreadySelectedCCs.includes("eu")){
|
||||||
|
alreadySelectedCCs = alreadySelectedCCs.filter(function(item){
|
||||||
|
return item != "eu";
|
||||||
|
});
|
||||||
|
alreadySelectedCCs = alreadySelectedCCs.concat(euCountries);
|
||||||
|
}
|
||||||
|
|
||||||
// loop through all items
|
// loop through all items
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
// get the value of the item (i.e. the country code)
|
// get the value of the item (i.e. the country code)
|
||||||
const code = item.dataset.value;
|
const code = item.dataset.value;
|
||||||
// if the code is in the array of codes to show, show the item
|
if (alreadySelectedCCs.includes(code)) {
|
||||||
if (codesToShow.includes(code)) {
|
//This country code already selected. Hide it
|
||||||
item.style.display = 'none';
|
item.style.display = 'none';
|
||||||
}
|
} else {
|
||||||
// otherwise, hide the item
|
// otherwise, show the item
|
||||||
else {
|
|
||||||
item.style.display = 'block';
|
item.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
<p style="margin-bottom: 0.4em;"><i class="ui upload icon"></i> Upload Default Keypairs</p>
|
<p style="margin-bottom: 0.4em;"><i class="ui upload icon"></i> Upload Default Keypairs</p>
|
||||||
<div class="ui buttons">
|
<div class="ui buttons">
|
||||||
<button class="ui basic grey button" onclick="uploadPublicKey();"><i class="globe icon"></i> Public Key</button>
|
<button class="ui basic grey button" onclick="uploadPublicKey();"><i class="globe icon"></i> Public Key</button>
|
||||||
<button class="ui basic black button" onclick="uploadPrivateKey();"><i class="black lock icon"></i> Private Key</button>
|
<button class="ui basic button" onclick="uploadPrivateKey();"><i class="grey lock icon"></i> Private Key</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<i class="ui green checkmark icon"></i> Redirection Rule Deleted
|
<i class="ui green checkmark icon"></i> Redirection Rule Deleted
|
||||||
</div>
|
</div>
|
||||||
<!-- Options -->
|
<!-- Options -->
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div class="ui accordion advanceSettings">
|
<div class="ui accordion advanceSettings">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
@ -173,7 +173,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (data.length == 0){
|
if (data.length == 0){
|
||||||
$("#redirectionRuleList").append(`<tr colspan="4"><td><i class="green check circle icon"></i> No redirection rule</td></tr>`);
|
$("#redirectionRuleList").append(`<tr><td colspan="5"><i class="green check circle icon"></i> No redirection rule</td></tr>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Advance configs -->
|
<!-- Advance configs -->
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div id="advanceProxyRules" class="ui fluid accordion">
|
<div id="advanceProxyRules" class="ui fluid accordion">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
@ -295,15 +295,25 @@
|
|||||||
//Automatic check if the site require TLS and check the checkbox if needed
|
//Automatic check if the site require TLS and check the checkbox if needed
|
||||||
function autoCheckTls(targetDomain){
|
function autoCheckTls(targetDomain){
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/proxy/tlscheck",
|
url: "/api/proxy/tlscheck?selfsignchk=true",
|
||||||
data: {url: targetDomain},
|
data: {url: targetDomain},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
msgbox(data.error, false);
|
msgbox(data.error, false);
|
||||||
}else if (data == "https"){
|
}else{
|
||||||
$("#reqTls").parent().checkbox("set checked");
|
//Check if the site require TLS
|
||||||
}else if (data == "http"){
|
if (data.protocol == "https"){
|
||||||
$("#reqTls").parent().checkbox("set unchecked");
|
$("#reqTls").parent().checkbox("set checked");
|
||||||
|
}else if (data.protocol == "http"){
|
||||||
|
$("#reqTls").parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
//Check if the site is using self-signed cert
|
||||||
|
if (data.selfsign){
|
||||||
|
$("#skipTLSValidation").parent().checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$("#skipTLSValidation").parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -73,28 +73,30 @@
|
|||||||
<p>Inbound Port (Reverse Proxy Listening Port)</p>
|
<p>Inbound Port (Reverse Proxy Listening Port)</p>
|
||||||
<div class="ui action fluid notloopbackOnly input" tourstep="incomingPort">
|
<div class="ui action fluid notloopbackOnly input" tourstep="incomingPort">
|
||||||
<small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small>
|
<small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small>
|
||||||
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
|
<input type="text" id="incomingPort" placeholder="Incoming Port" value="443">
|
||||||
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
|
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div id="tls" class="ui toggle notloopbackOnly checkbox">
|
<div id="tls" class="ui toggle notloopbackOnly checkbox">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Use TLS to serve proxy request</label>
|
<label>Use TLS to serve proxy request<br>
|
||||||
|
<small>Also known as HTTPS mode</small></label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;" >
|
<div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.4em;" >
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Enable HTTP server on port 80<br>
|
<label>Enable HTTP server on port 80<br>
|
||||||
<small>(Only apply when TLS enabled and not using port 80)</small></label>
|
<small>Accept HTTP requests even if you are using HTTPS mode</small></label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div tourstep="forceHttpsRedirect" style="display: inline-block;">
|
<div tourstep="forceHttpsRedirect" style="display: inline-block;">
|
||||||
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;">
|
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.4em;">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Force redirect HTTP request to HTTPS</label>
|
<label>Force redirect HTTP request to HTTPS<br>
|
||||||
|
<small>Redirect web traffic from port 80 to 443, require enabling HTTP server on port 80</small></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div class="ui accordion advanceSettings">
|
<div class="ui accordion advanceSettings">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
@ -359,10 +361,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (enabled){
|
if (enabled){
|
||||||
$("#redirect").show();
|
//$("#redirect").show();
|
||||||
msgbox("Port 80 listener enabled");
|
msgbox("Port 80 listener enabled");
|
||||||
}else{
|
}else{
|
||||||
$("#redirect").hide();
|
//$("#redirect").hide();
|
||||||
msgbox("Port 80 listener disabled");
|
msgbox("Port 80 listener disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,10 +402,10 @@
|
|||||||
$.get("/api/proxy/listenPort80", function(data){
|
$.get("/api/proxy/listenPort80", function(data){
|
||||||
if (data){
|
if (data){
|
||||||
$("#listenP80").checkbox("set checked");
|
$("#listenP80").checkbox("set checked");
|
||||||
$("#redirect").show();
|
//$("#redirect").show();
|
||||||
}else{
|
}else{
|
||||||
$("#listenP80").checkbox("set unchecked");
|
$("#listenP80").checkbox("set unchecked");
|
||||||
$("#redirect").hide();
|
//$("#redirect").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#listenP80").find("input").on("change", function(){
|
$("#listenP80").find("input").on("change", function(){
|
||||||
@ -579,7 +581,7 @@
|
|||||||
let timestamps = [];
|
let timestamps = [];
|
||||||
|
|
||||||
for(var i = 0; i < dataCount; i++){
|
for(var i = 0; i < dataCount; i++){
|
||||||
timestamps.push(parseInt(Date.now() / 1000) + i);
|
timestamps.push(new Date(Date.now() + i * 1000).toLocaleString().replace(',', ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchData() {
|
function fetchData() {
|
||||||
@ -600,10 +602,8 @@
|
|||||||
txValues.shift();
|
txValues.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timestamps.push(new Date(Date.now()).toLocaleString().replace(',', ''));
|
||||||
timestamps.push(parseInt(Date.now() / 1000));
|
|
||||||
timestamps.shift();
|
timestamps.shift();
|
||||||
|
|
||||||
updateChart();
|
updateChart();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
initUptimeTable();
|
initUptimeTable();
|
||||||
|
|
||||||
function reloadUptimeList(){
|
function reloadUptimeList(){
|
||||||
$("#utmrender").html(`<div class="ui segment">
|
$("#utmrender").html(`<div class="ui utmloading segment">
|
||||||
<div class="ui active inverted dimmer" style="z-index: 2;">
|
<div class="ui active inverted dimmer" style="z-index: 2;">
|
||||||
<div class="ui text loader">Loading</div>
|
<div class="ui text loader">Loading</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Advance configs -->
|
<!-- Advance configs -->
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div id="advanceProxyRules" class="ui fluid accordion">
|
<div id="advanceProxyRules" class="ui fluid accordion">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
@ -191,14 +191,19 @@
|
|||||||
var targetDomain = $("#virtualDirectoryDomain").val().trim();
|
var targetDomain = $("#virtualDirectoryDomain").val().trim();
|
||||||
if (targetDomain != ""){
|
if (targetDomain != ""){
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/proxy/tlscheck",
|
url: "/api/proxy/tlscheck?selfsignchk=true",
|
||||||
data: {url: targetDomain},
|
data: {url: targetDomain},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
|
|
||||||
}else if (data == "https"){
|
}else if (data.protocol == "https"){
|
||||||
$("#vdReqTls").parent().checkbox("set checked");
|
$("#vdReqTls").parent().checkbox("set checked");
|
||||||
}else if (data == "http"){
|
if (data.selfsign){
|
||||||
|
$("#vdSkipTLSValidation").parent().checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$("#vdSkipTLSValidation").parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
}else if (data.protocol == "http"){
|
||||||
$("#vdReqTls").parent().checkbox("set unchecked");
|
$("#vdReqTls").parent().checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1133
src/web/darktheme.css
Normal file
1133
src/web/darktheme.css
Normal file
File diff suppressed because it is too large
Load Diff
@ -16,22 +16,24 @@
|
|||||||
<script src="script/chart.js"></script>
|
<script src="script/chart.js"></script>
|
||||||
<script src="script/utils.js"></script>
|
<script src="script/utils.js"></script>
|
||||||
<link rel="stylesheet" href="main.css">
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<link rel="stylesheet" href="darktheme.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="script/darktheme.js"></script>
|
||||||
<div class="menubar">
|
<div class="menubar">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<img class="logo" src="img/logo.svg">
|
<img class="logo" src="img/logo.svg">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui right floated buttons menutoggle" style="padding-top: 2px;">
|
<div class="ui right floated buttons menutoggle" style="padding-top: 2px;">
|
||||||
<button class="ui basic icon button" onclick="$('.toolbar').fadeToggle('fast');"><i class="content icon"></i></button>
|
<button id="sidemenuBtn" class="ui basic icon button" onclick="$('.toolbar').fadeToggle('fast');"><i class="content icon"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui right floated buttons" style="padding-top: 2px; padding-right: 0.4em;">
|
<div class="ui right floated buttons" style="padding-top: 2px; padding-right: 0.4em;">
|
||||||
<button class="ui basic white icon button" onclick="logout();"><i class="sign-out icon"></i></button>
|
<button class="ui basic white icon button" onclick="logout();"><i class="sign-out icon"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="ui right floated buttons" style="padding-top: 2px;">
|
<div class="ui right floated buttons" style="padding-top: 2px; margin-right: 0.4em;">
|
||||||
<button id="themeColorButton" class="ui icon button" onclick="toggleTheme();"><i class="sun icon"></i></button>
|
<button id="themeColorButton" class="ui icon button" onclick="toggleTheme();"><i class="sun icon"></i></button>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
@ -269,11 +271,18 @@
|
|||||||
|
|
||||||
function toggleTheme(){
|
function toggleTheme(){
|
||||||
if ($("body").hasClass("darkTheme")){
|
if ($("body").hasClass("darkTheme")){
|
||||||
$("body").removeClass("darkTheme")
|
setDarkTheme(false);
|
||||||
$("#themeColorButton").html(`<i class="ui moon icon"></i>`);
|
//Check if the snippet iframe is opened. If yes, set the dark theme to the iframe
|
||||||
|
if ($(".sideWrapper").is(":visible")){
|
||||||
|
$(".sideWrapper iframe")[0].contentWindow.setDarkTheme(false);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
$("body").addClass("darkTheme");
|
setDarkTheme(true);
|
||||||
$("#themeColorButton").html(`<i class="ui sun icon"></i>`);
|
//Check if the snippet iframe is opened. If yes, set the dark theme to the iframe
|
||||||
|
if ($(".sideWrapper").is(":visible")){
|
||||||
|
$(".sideWrapper iframe")[0].contentWindow.setDarkTheme(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,50 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
index.html style overwrite
|
index.html style overwrite
|
||||||
*/
|
*/
|
||||||
:root{
|
|
||||||
--theme_background: linear-gradient(60deg, rgb(84, 58, 183) 0%, rgb(0, 172, 193) 100%);
|
|
||||||
--theme_background_inverted: linear-gradient(215deg, rgba(38,60,71,1) 13%, rgba(2,3,42,1) 84%);
|
|
||||||
--theme_green: linear-gradient(270deg, #27e7ff, #00ca52);
|
|
||||||
--theme_red: linear-gradient(203deg, rgba(250,172,38,1) 17%, rgba(202,0,37,1) 78%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Theme Color Definations */
|
/* Theme color palletes are defined in darktheme.css */
|
||||||
body:not(.darkTheme){
|
|
||||||
--theme_bg: #f6f6f6;
|
|
||||||
--theme_bg_primary: #ffffff;
|
|
||||||
--theme_bg_secondary: #ffffff;
|
|
||||||
--theme_bg_active: #ececec;
|
|
||||||
--theme_highlight: #a9d1f3;
|
|
||||||
--theme_bg_inverted: #27292d;
|
|
||||||
--theme_advance: #f8f8f9;
|
|
||||||
--item_color: #5e5d5d;
|
|
||||||
--item_color_select: rgba(0,0,0,.87);
|
|
||||||
--text_color: #414141;
|
|
||||||
--input_color: white;
|
|
||||||
--divider_color: #cacaca;
|
|
||||||
--text_color_inverted: #fcfcfc;
|
|
||||||
--button_text_color: #878787;
|
|
||||||
--button_border_color: #dedede;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.darkTheme{
|
|
||||||
--theme_bg: #27292d;
|
|
||||||
--theme_bg_primary: #3d3f47;
|
|
||||||
--theme_bg_secondary: #373a42;
|
|
||||||
--theme_highlight: #6682c4;
|
|
||||||
--theme_bg_active: #292929;
|
|
||||||
--theme_bg_inverted: #f8f8f9;
|
|
||||||
--theme_advance: #333333;
|
|
||||||
--item_color: #cacaca;
|
|
||||||
--text_color: #fcfcfc;
|
|
||||||
--text_color_secondary: #dfdfdf;
|
|
||||||
--input_color: black;
|
|
||||||
--divider_color: #3b3b3b;
|
|
||||||
--item_color_select: rgba(255, 255, 255, 0.87);
|
|
||||||
--text_color_inverted: #414141;
|
|
||||||
--button_text_color: #e9e9e9;
|
|
||||||
--button_border_color: #646464;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Theme Toggle CSS */
|
/* Theme Toggle CSS */
|
||||||
#themeColorButton{
|
#themeColorButton{
|
||||||
@ -368,7 +326,7 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.basic.segment.advanceoptions{
|
.basic.segment.advanceoptions{
|
||||||
background-color: #f7f7f7;
|
background-color: var(--theme_advance);
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
src/web/robots.txt
Normal file
6
src/web/robots.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# robots.txt for Zoraxy project
|
||||||
|
# In general, you should not expose the management interface to the internet.
|
||||||
|
# In case you do, this file (hopefully) protects you from web crawlers.
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
51
src/web/script/darktheme.js
Normal file
51
src/web/script/darktheme.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Dark Theme Toggle Manager
|
||||||
|
|
||||||
|
This script is used to manage the dark theme toggle button in the header of the website.
|
||||||
|
It will change the theme of the website to dark mode when the toggle is clicked and back to light mode when clicked again.
|
||||||
|
|
||||||
|
Must be included just after the start of body tag in the HTML file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function _whiteThemeHandleApplyChange(){
|
||||||
|
$(".menubar .logo").attr("src", "img/logo.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
function _darkThemeHandleApplyChange(){
|
||||||
|
$(".menubar .logo").attr("src", "img/logo_white.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Check if the theme is dark, must be done before the body is loaded to prevent flickering
|
||||||
|
function setDarkTheme(isDarkTheme = false){
|
||||||
|
if (isDarkTheme){
|
||||||
|
$("body").addClass("darkTheme");
|
||||||
|
$("#themeColorButton").html(`<i class="ui sun icon"></i>`);
|
||||||
|
localStorage.setItem("theme", "dark");
|
||||||
|
|
||||||
|
//Check if the page is still loading, if not change the logo
|
||||||
|
if (document.readyState == "complete"){
|
||||||
|
_darkThemeHandleApplyChange();
|
||||||
|
}else{
|
||||||
|
//Wait for the page to load and then change the logo
|
||||||
|
$(document).ready(function(){
|
||||||
|
_darkThemeHandleApplyChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$("body").removeClass("darkTheme")
|
||||||
|
$("#themeColorButton").html(`<i class="ui moon icon"></i>`);
|
||||||
|
localStorage.setItem("theme", "light");
|
||||||
|
//By default the page is white theme. So no need to change the logo if page is still loading
|
||||||
|
if (document.readyState == "complete"){
|
||||||
|
//Switching back to light theme
|
||||||
|
_whiteThemeHandleApplyChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.getItem("theme") == "dark"){
|
||||||
|
setDarkTheme(true);
|
||||||
|
}else{
|
||||||
|
setDarkTheme(false);
|
||||||
|
}
|
@ -14,69 +14,72 @@
|
|||||||
top: 0.4em;
|
top: 0.4em;
|
||||||
right: 1em;
|
right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<br>
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
<div class="ui container">
|
<script src="../script/darktheme.js"></script>
|
||||||
<div class="ui header">
|
|
||||||
<div class="content">
|
|
||||||
Access Rule Editor
|
|
||||||
<div class="sub header">Create, Edit or Remove Access Rules</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
<div class="ui top attached tabular menu">
|
|
||||||
<a class="active item" data-tab="new"><i class="ui green add icon"></i> New</a>
|
|
||||||
<a class="item" data-tab="edit"><i class="ui grey edit icon"></i> Edit</a>
|
|
||||||
</div>
|
|
||||||
<div class="ui bottom attached active tab segment" data-tab="new">
|
|
||||||
<p>Create a new Access Rule</p>
|
|
||||||
<form class="ui form" id="accessRuleForm">
|
|
||||||
<div class="field">
|
|
||||||
<label>Rule Name</label>
|
|
||||||
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea name="description" placeholder="Description" required></textarea>
|
|
||||||
</div>
|
|
||||||
<button class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
|
||||||
</form>
|
|
||||||
<br>
|
<br>
|
||||||
</div>
|
<div class="ui container">
|
||||||
<div class="ui bottom attached tab segment" data-tab="edit">
|
<div class="ui header">
|
||||||
<p>Select an Access Rule to edit</p>
|
<div class="content">
|
||||||
<button id="refreshAccessRuleListBtn" class="ui circular basic icon button" onclick="reloadAccessRuleList()"><i class="ui green refresh icon"></i></button>
|
Access Rule Editor
|
||||||
<div class="ui selection fluid dropdown" id="accessRuleSelector">
|
<div class="sub header">Create, Edit or Remove Access Rules</div>
|
||||||
<input type="hidden" name="targetAccessRule" value="default">
|
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
<div class="default text"></div>
|
|
||||||
<div class="menu" id="accessRuleList">
|
|
||||||
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="ui top attached tabular menu">
|
||||||
|
<a class="active item" data-tab="new"><i class="ui green add icon"></i> New</a>
|
||||||
|
<a class="item" data-tab="edit"><i class="ui grey edit icon"></i> Edit</a>
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached active tab segment" data-tab="new">
|
||||||
|
<p>Create a new Access Rule</p>
|
||||||
|
<form class="ui form" id="accessRuleForm">
|
||||||
|
<div class="field">
|
||||||
|
<label>Rule Name</label>
|
||||||
|
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Description</label>
|
||||||
|
<textarea name="description" placeholder="Description" required></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached tab segment" data-tab="edit">
|
||||||
|
<p>Select an Access Rule to edit</p>
|
||||||
|
<button id="refreshAccessRuleListBtn" class="ui circular basic icon button" onclick="reloadAccessRuleList()"><i class="ui green refresh icon"></i></button>
|
||||||
|
<div class="ui selection fluid dropdown" id="accessRuleSelector">
|
||||||
|
<input type="hidden" name="targetAccessRule" value="default">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text"></div>
|
||||||
|
<div class="menu" id="accessRuleList">
|
||||||
|
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<form class="ui form" id="modifyRuleInfo">
|
||||||
|
<div class="disabled field">
|
||||||
|
<label>Rule ID</label>
|
||||||
|
<input type="text" name="accessRuleUUID">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Rule Name</label>
|
||||||
|
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Description</label>
|
||||||
|
<textarea name="description" placeholder="Description" required></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="ui basic button" type="submit"><i class="ui green save icon"></i> Save Changes</button>
|
||||||
|
<button class="ui basic button" onclick="removeAccessRule(event);"><i class="ui red trash icon"></i> Remove Rule</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<form class="ui form" id="modifyRuleInfo">
|
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Close</button>
|
||||||
<div class="disabled field">
|
<br><br><br>
|
||||||
<label>Rule ID</label>
|
|
||||||
<input type="text" name="accessRuleUUID">
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Rule Name</label>
|
|
||||||
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea name="description" placeholder="Description" required></textarea>
|
|
||||||
</div>
|
|
||||||
<button class="ui basic button" type="submit"><i class="ui green save icon"></i> Save Changes</button>
|
|
||||||
<button class="ui basic button" onclick="removeAccessRule(event);"><i class="ui red trash icon"></i> Remove Rule</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Close</button>
|
|
||||||
<br><br><br>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
@ -50,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<small>If you don't want to share your private email address, you can also fill in an email address that point to a mailbox not exists on your domain.</small>
|
<small>If you don't want to share your private email address, you can also fill in an email address that point to a mailbox not exists on your domain.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div class="ui accordion advanceSettings">
|
<div class="ui accordion advanceSettings">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
@ -437,11 +439,15 @@
|
|||||||
let optionalFieldsHTML = "";
|
let optionalFieldsHTML = "";
|
||||||
for (const [key, datatype] of Object.entries(data)) {
|
for (const [key, datatype] of Object.entries(data)) {
|
||||||
if (datatype == "int"){
|
if (datatype == "int"){
|
||||||
$("#dnsProviderAPIFields").append(`<div class="ui fluid labeled dnsConfigField input" key="${key}" style="margin-top: 0.2em;">
|
let defaultValue = 10;
|
||||||
|
if (key == "HTTPTimeout"){
|
||||||
|
defaultValue = 300;
|
||||||
|
}
|
||||||
|
$("#dnsProviderAPIFields").append(`<div class="ui fluid labeled dnsConfigField input typeint" key="${key}" style="margin-top: 0.2em;">
|
||||||
<div class="ui basic blue label" style="font-weight: 300;">
|
<div class="ui basic blue label" style="font-weight: 300;">
|
||||||
${key}
|
${key}
|
||||||
</div>
|
</div>
|
||||||
<input type="number" value="300">
|
<input type="number" value="${defaultValue}">
|
||||||
</div>`);
|
</div>`);
|
||||||
}else if (datatype == "bool"){
|
}else if (datatype == "bool"){
|
||||||
booleanFieldsHTML += (`<div class="ui checkbox dnsConfigField" key="${key}" style="margin-top: 1em !important; padding-left: 0.4em;">
|
booleanFieldsHTML += (`<div class="ui checkbox dnsConfigField" key="${key}" style="margin-top: 1em !important; padding-left: 0.4em;">
|
||||||
@ -600,8 +606,12 @@
|
|||||||
//Boolean option
|
//Boolean option
|
||||||
let checked = $(this).find("input")[0].checked;
|
let checked = $(this).find("input")[0].checked;
|
||||||
dnsCredentials[thisKey] = checked;
|
dnsCredentials[thisKey] = checked;
|
||||||
|
}else if ($(this).hasClass("typeint")){
|
||||||
|
//Int options
|
||||||
|
let value = $(this).find("input").val();
|
||||||
|
dnsCredentials[thisKey] = parseInt(value);
|
||||||
}else{
|
}else{
|
||||||
//String or int options
|
//String options
|
||||||
let value = $(this).find("input").val().trim();
|
let value = $(this).find("input").val().trim();
|
||||||
dnsCredentials[thisKey] = value;
|
dnsCredentials[thisKey] = value;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<script src="../script/utils.js"></script>
|
<script src="../script/utils.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<script src="../script/utils.js"></script>
|
<script src="../script/utils.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<script src="../script/utils.js"></script>
|
<script src="../script/utils.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<script src="../script/utils.js"></script>
|
<script src="../script/utils.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -23,9 +23,15 @@
|
|||||||
#permissionPolicyEditor .experimental{
|
#permissionPolicyEditor .experimental{
|
||||||
background-color: rgb(241, 241, 241);
|
background-color: rgb(241, 241, 241);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.darkTheme #permissionPolicyEditor .experimental{
|
||||||
|
background-color: rgb(41, 41, 41);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
@ -86,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment advanceoptions">
|
||||||
<div class="ui fluid accordion">
|
<div class="ui fluid accordion">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<i class="dropdown icon" tabindex="0"><div class="menu" tabindex="-1"></div></i>
|
<i class="dropdown icon" tabindex="0"><div class="menu" tabindex="-1"></div></i>
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
<script src="../script/semantic/semantic.min.js"></script>
|
<script src="../script/semantic/semantic.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br />
|
<br />
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
|
@ -41,9 +41,28 @@
|
|||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.darkTheme #accessRuleList{
|
||||||
|
border: 1px solid rgb(50, 50, 50) !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .ui.segment.accessRule {
|
||||||
|
border: 1px solid var(--theme_bg_secondary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .ui.segment.accessRule:hover {
|
||||||
|
background-color: var(--theme_bg_secondary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .ui.segment.accessRule.active {
|
||||||
|
background-color: var(--theme_bg_secondary) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
<script src="../script/semantic/semantic.min.js"></script>
|
<script src="../script/semantic/semantic.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -55,10 +55,64 @@
|
|||||||
background:#3643bb;
|
background:#3643bb;
|
||||||
color:white;
|
color:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist {
|
||||||
|
background-color: #1b1c1d;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.header .content .sub.header {
|
||||||
|
color: #bbbbbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.divider {
|
||||||
|
border-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.accordion .title,
|
||||||
|
body.darkTheme .loglist .ui.accordion .content {
|
||||||
|
background-color: #1b1c1d;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.accordion .title:hover {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.list .item {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.list .item .content {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.list .item .showing {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.button.filterbtn {
|
||||||
|
background-color: #333333;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.button.filterbtn:hover {
|
||||||
|
background-color: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist .ui.toggle.checkbox label {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.darkTheme .loglist small {
|
||||||
|
color: #bbbbbb;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui stackable grid">
|
<div class="ui stackable grid">
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div class="ui active inverted dimmer">
|
<div class="ui active inverted dimmer">
|
||||||
<div class="ui text loader">Loading Snippet</div>
|
<div class="ui text loader">Loading Snippet</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
|
@ -56,6 +56,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Color definations
|
Color definition
|
||||||
*/
|
*/
|
||||||
:root{
|
:root{
|
||||||
--dark_theme_toggle: #333333;
|
--dark_theme_toggle: #333333;
|
||||||
@ -1252,6 +1252,10 @@ code{
|
|||||||
color: var(--um_file_txt) !important;
|
color: var(--um_file_txt) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.darkTheme .filename{
|
||||||
|
color:var(--text_color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Hot Search Related
|
Hot Search Related
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="whiteTheme">
|
<body class="whiteTheme">
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div id="navibar" class="navibar">
|
<div id="navibar" class="navibar">
|
||||||
<!-- File Opr Group-->
|
<!-- File Opr Group-->
|
||||||
<button class="fileOprBtn" title="Open" onclick="openViaButton(event);"><img class="opricon" src="img/opr/open.svg"><p class="oprtxt" locale="fileopr/Open">Open</p></button>
|
<button class="fileOprBtn" title="Open" onclick="openViaButton(event);"><img class="opricon" src="img/opr/open.svg"><p class="oprtxt" locale="fileopr/Open">Open</p></button>
|
||||||
@ -60,7 +62,7 @@
|
|||||||
<div id="pathDisplayField" class="ui breadcrumb addressText pathDisplay desktopOnly" >
|
<div id="pathDisplayField" class="ui breadcrumb addressText pathDisplay desktopOnly" >
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button id="togglePropertiesViewBtn" style="margin-left: 0.4em;" class="ui icon tiny button videmode propbar" title="Show Properties" onclick="togglePropertiesView(this);"><i class="columns icon"></i></button>
|
<button id="togglePropertiesViewBtn" style="margin-left: 0.4em; " class="ui icon tiny button videmode propbar" title="Show Properties" onclick="togglePropertiesView(this);"><i class="columns icon"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="msgbox" style="z-index:999; display:none; padding-bottom: 1em;">
|
<div class="msgbox" style="z-index:999; display:none; padding-bottom: 1em;">
|
||||||
@ -79,7 +81,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
<div id="propertiesView" class="small">
|
<div id="propertiesView" class="small" style="height: 100%;">
|
||||||
<h3 class="ui header" style="margin-top: 0.4em;">
|
<h3 class="ui header" style="margin-top: 0.4em;">
|
||||||
<span class="filename" style="word-break: break-all;" locale="sidebar/default/nofileselected">No File Selected</span>
|
<span class="filename" style="word-break: break-all;" locale="sidebar/default/nofileselected">No File Selected</span>
|
||||||
<div class="sub header vpath" style="word-break: break-all;" locale="sidebar/default/instruction">Select a file to view file properties</div>
|
<div class="sub header vpath" style="word-break: break-all;" locale="sidebar/default/instruction">Select a file to view file properties</div>
|
||||||
@ -471,11 +473,12 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>`);
|
</div>`);
|
||||||
}else{
|
}else{
|
||||||
|
let isDarkTheme = $("body").hasClass("darkTheme");
|
||||||
let extension = "." + filename.split(".").pop();
|
let extension = "." + filename.split(".").pop();
|
||||||
let fileIcon = getFileIcon(extension);
|
let fileIcon = getFileIcon(extension);
|
||||||
$("#fileList").append(`<div class="fileObject item" draggable="true" filename="${filename}" filepath="${path + filename}" ondblclick="openthis(this,event);" type="file">
|
$("#fileList").append(`<div class="fileObject item" draggable="true" filename="${filename}" filepath="${path + filename}" ondblclick="openthis(this,event);" type="file">
|
||||||
<span style="display:inline-block !important;word-break: break-all; width:100%;" class="normal object">
|
<span style="display:inline-block !important;word-break: break-all; width:100%;" class="normal object">
|
||||||
<i class="${fileIcon} icon" style="margin-right:12px; color:grey;"></i> <span class="filename">${filename} (${humanFileSize(filesize)})</span>
|
<i class="${fileIcon} icon" style="margin-right:12px; color:${isDarkTheme?'white':'grey'} !important;"></i> <span class="filename">${filename} (${humanFileSize(filesize)})</span>
|
||||||
</span>
|
</span>
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
<link rel="stylesheet" href="../main.css">
|
<link rel="stylesheet" href="../main.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui yellow message">
|
<div class="ui yellow message">
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<br>
|
<br>
|
||||||
<div class="ui segment">
|
<div class="ui segment">
|
||||||
|
@ -22,8 +22,10 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div id="mdns-hosts">
|
<div id="mdns-hosts">
|
||||||
|
<span><i class="ui loading spinner icon"></i> Loading</span>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
</head>
|
</head>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<br>
|
<br>
|
||||||
<div class="ui segment">
|
<div class="ui segment">
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<link rel="stylesheet" href="../darktheme.css">
|
||||||
|
<script src="../script/darktheme.js"></script>
|
||||||
<div id="loadingUI">
|
<div id="loadingUI">
|
||||||
<div style="margin-top: 2em; margin-left: 2em; color: white;">
|
<div style="margin-top: 2em; margin-left: 2em; color: white;">
|
||||||
<i class="ui loading spinner icon"></i> <b>Creating virtual terminal session</b><br>
|
<i class="ui loading spinner icon"></i> <b>Creating virtual terminal session</b><br>
|
||||||
|
@ -18,11 +18,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy"
|
"imuslab.com/zoraxy/mod/dynamicproxy"
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
|
||||||
@ -32,39 +30,6 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/wakeonlan"
|
"imuslab.com/zoraxy/mod/wakeonlan"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Proxy Utils
|
|
||||||
*/
|
|
||||||
//Check if site support TLS
|
|
||||||
func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
|
|
||||||
targetURL, err := utils.PostPara(r, "url")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "invalid url given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpsUrl := fmt.Sprintf("https://%s", targetURL)
|
|
||||||
httpUrl := fmt.Sprintf("http://%s", targetURL)
|
|
||||||
|
|
||||||
client := http.Client{Timeout: 5 * time.Second}
|
|
||||||
|
|
||||||
resp, err := client.Head(httpsUrl)
|
|
||||||
if err == nil && resp.StatusCode == http.StatusOK {
|
|
||||||
js, _ := json.Marshal("https")
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err = client.Head(httpUrl)
|
|
||||||
if err == nil && resp.StatusCode == http.StatusOK {
|
|
||||||
js, _ := json.Marshal("http")
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendErrorResponse(w, "invalid url given")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Statistic Summary
|
Statistic Summary
|
||||||
*/
|
*/
|
||||||
@ -367,12 +332,22 @@ func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
ZerotierConnected bool
|
ZerotierConnected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayUUID := nodeUUID
|
||||||
|
displayAllowSSHLB := *allowSshLoopback
|
||||||
|
displayBootTime := bootTime
|
||||||
|
|
||||||
|
if !authAgent.CheckAuth(r) {
|
||||||
|
displayUUID = "Unauthorized"
|
||||||
|
displayAllowSSHLB = false
|
||||||
|
displayBootTime = 0
|
||||||
|
}
|
||||||
|
|
||||||
info := ZoraxyInfo{
|
info := ZoraxyInfo{
|
||||||
Version: version,
|
Version: SYSTEM_VERSION,
|
||||||
NodeUUID: nodeUUID,
|
NodeUUID: displayUUID,
|
||||||
Development: development,
|
Development: DEVELOPMENT_BUILD,
|
||||||
BootTime: bootTime,
|
BootTime: displayBootTime,
|
||||||
EnableSshLoopback: *allowSshLoopback,
|
EnableSshLoopback: displayAllowSSHLB,
|
||||||
ZerotierConnected: ganManager.ControllerID != "",
|
ZerotierConnected: ganManager.ControllerID != "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user