diff --git a/src/acme.go b/src/acme.go index a04a257..cdd04dc 100644 --- a/src/acme.go +++ b/src/acme.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "log" "math/rand" "net/http" "regexp" @@ -29,7 +28,7 @@ func getRandomPort(minPort int) int { // init the new ACME instance func initACME() *acme.ACMEHandler { - log.Println("Starting ACME handler") + SystemWideLogger.Println("Starting ACME handler") rand.Seed(time.Now().UnixNano()) // Generate a random port above 30000 port := getRandomPort(30000) @@ -44,7 +43,7 @@ func initACME() *acme.ACMEHandler { // create the special routing rule for ACME func acmeRegisterSpecialRoutingRule() { - log.Println("Assigned temporary port:" + acmeHandler.Getport()) + SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport()) err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{ ID: "acme-autorenew", @@ -79,7 +78,7 @@ func acmeRegisterSpecialRoutingRule() { }) if err != nil { - log.Println("[Err] " + err.Error()) + SystemWideLogger.PrintAndLog("ACME", "Unable register temp port for DNS resolver", err) } } @@ -89,7 +88,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) if dynamicProxyRouter.Option.Port == 443 { //Enable port 80 to 443 redirect if !dynamicProxyRouter.Option.ForceHttpsRedirect { - log.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests") + SystemWideLogger.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true) } else { //Set this to true, so after renew, do not turn it off @@ -110,7 +109,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) if dynamicProxyRouter.Option.Port == 443 { if !isForceHttpsRedirectEnabledOriginally { //Default is off. Turn the redirection off - log.Println("Restoring HTTP to HTTPS redirect settings") + SystemWideLogger.PrintAndLog("ACME", "Restoring HTTP to HTTPS redirect settings", nil) dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false) } } @@ -130,7 +129,7 @@ func HandleACMEPreferredCA(w http.ResponseWriter, r *http.Request) { acme.IsSupportedCA(ca) //Set the new config sysdb.Write("acmepref", "prefca", ca) - log.Println("Updating prefered ACME CA to " + ca) + SystemWideLogger.Println("Updating prefered ACME CA to " + ca) utils.SendOK(w) } diff --git a/src/cert.go b/src/cert.go index e0f3464..567fd7c 100644 --- a/src/cert.go +++ b/src/cert.go @@ -6,7 +6,6 @@ import ( "encoding/pem" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -128,7 +127,7 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) { certBtyes, err := os.ReadFile(certFilepath) if err != nil { // Unable to load this file - log.Println("Unable to load certificate: " + certFilepath) + SystemWideLogger.PrintAndLog("TLS", "Unable to load certificate: "+certFilepath, err) continue } else { // Cert loaded. Check its expiry time @@ -182,11 +181,11 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) { } else { if newState == "true" { sysdb.Write("settings", "usetls", true) - log.Println("Enabling TLS mode on reverse proxy") + SystemWideLogger.Println("Enabling TLS mode on reverse proxy") dynamicProxyRouter.UpdateTLSSetting(true) } else if newState == "false" { sysdb.Write("settings", "usetls", false) - log.Println("Disabling TLS mode on reverse proxy") + SystemWideLogger.Println("Disabling TLS mode on reverse proxy") dynamicProxyRouter.UpdateTLSSetting(false) } else { utils.SendErrorResponse(w, "invalid state given. Only support true or false") @@ -213,11 +212,11 @@ func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) { } else { if newState == "true" { sysdb.Write("settings", "forceLatestTLS", true) - log.Println("Updating minimum TLS version to v1.2 or above") + SystemWideLogger.Println("Updating minimum TLS version to v1.2 or above") dynamicProxyRouter.UpdateTLSVersion(true) } else if newState == "false" { sysdb.Write("settings", "forceLatestTLS", false) - log.Println("Updating minimum TLS version to v1.0 or above") + SystemWideLogger.Println("Updating minimum TLS version to v1.0 or above") dynamicProxyRouter.UpdateTLSVersion(false) } else { utils.SendErrorResponse(w, "invalid state given") diff --git a/src/config.go b/src/config.go index 9150476..c012467 100644 --- a/src/config.go +++ b/src/config.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -29,6 +28,7 @@ type Record struct { Rootname string ProxyTarget string UseTLS bool + BypassGlobalTLS bool SkipTlsValidation bool RequireBasicAuth bool BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials @@ -61,11 +61,11 @@ func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) e func RemoveReverseProxyConfigFile(rootname string) error { filename := getFilenameFromRootName(rootname) removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/") - log.Println("Config Removed: ", removePendingFile) + SystemWideLogger.Println("Config Removed: ", removePendingFile) if utils.FileExists(removePendingFile) { err := os.Remove(removePendingFile) if err != nil { - log.Println(err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err) return err } } @@ -81,6 +81,7 @@ func LoadReverseProxyConfig(filename string) (*Record, error) { Rootname: "", ProxyTarget: "", UseTLS: false, + BypassGlobalTLS: false, SkipTlsValidation: false, RequireBasicAuth: false, BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{}, @@ -109,6 +110,7 @@ func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoin Rootname: targetProxyEndpoint.RootOrMatchingDomain, ProxyTarget: targetProxyEndpoint.Domain, UseTLS: targetProxyEndpoint.RequireTLS, + BypassGlobalTLS: targetProxyEndpoint.BypassGlobalTLS, SkipTlsValidation: targetProxyEndpoint.SkipCertValidations, RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth, BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials, @@ -191,14 +193,14 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) { //Also zip in the sysdb zipFile, err := zipWriter.Create("sys.db") if err != nil { - log.Println("[Backup] Unable to zip sysdb: " + err.Error()) + SystemWideLogger.PrintAndLog("Backup", "Unable to zip sysdb", err) return } // Open the file on disk file, err := os.Open("sys.db") if err != nil { - log.Println("[Backup] Unable to open sysdb: " + err.Error()) + SystemWideLogger.PrintAndLog("Backup", "Unable to open sysdb", err) return } defer file.Close() @@ -206,7 +208,7 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) { // Copy the file contents to the zip file _, err = io.Copy(zipFile, file) if err != nil { - log.Println(err) + SystemWideLogger.Println(err) return } @@ -311,12 +313,12 @@ func ImportConfigFromZip(w http.ResponseWriter, r *http.Request) { // Send a success response w.WriteHeader(http.StatusOK) - log.Println("Configuration restored") + SystemWideLogger.Println("Configuration restored") fmt.Fprintln(w, "Configuration restored") if restoreDatabase { go func() { - log.Println("Database altered. Restarting in 3 seconds...") + SystemWideLogger.Println("Database altered. Restarting in 3 seconds...") time.Sleep(3 * time.Second) os.Exit(0) }() diff --git a/src/main.go b/src/main.go index 45d5ec5..4803065 100644 --- a/src/main.go +++ b/src/main.go @@ -20,6 +20,7 @@ import ( "imuslab.com/zoraxy/mod/email" "imuslab.com/zoraxy/mod/ganserv" "imuslab.com/zoraxy/mod/geodb" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/mdns" "imuslab.com/zoraxy/mod/netstat" "imuslab.com/zoraxy/mod/pathrule" @@ -44,6 +45,7 @@ var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL cert 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 logOutputToFile = flag.Bool("log", true, "Log terminal output to file") var ( name = "Zoraxy" @@ -80,8 +82,9 @@ var ( staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs //Helper modules - EmailSender *email.Sender //Email sender that handle email sending - AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic + EmailSender *email.Sender //Email sender that handle email sending + AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic + SystemWideLogger *logger.Logger //Logger for Zoraxy ) // Kill signal handler. Do something before the system the core terminate. @@ -116,6 +119,9 @@ func ShutdownSeq() { fmt.Println("- Cleaning up tmp files") os.RemoveAll("./tmp") + fmt.Println("- Closing system wide logger") + SystemWideLogger.Close() + //Close database, final fmt.Println("- Stopping system database") sysdb.Close() @@ -151,7 +157,7 @@ func main() { } uuidBytes, err := os.ReadFile(uuidRecord) if err != nil { - log.Println("Unable to read system uuid from file system") + SystemWideLogger.PrintAndLog("ZeroTier", "Unable to read system uuid from file system", nil) panic(err) } nodeUUID = string(uuidBytes) @@ -173,7 +179,7 @@ func main() { //Start the finalize sequences finalSequence() - log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port) + SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port) err = http.ListenAndServe(handler.Port, nil) if err != nil { diff --git a/src/mod/dynamicproxy/subdomain.go b/src/mod/dynamicproxy/subdomain.go index 9012724..f01e541 100644 --- a/src/mod/dynamicproxy/subdomain.go +++ b/src/mod/dynamicproxy/subdomain.go @@ -38,6 +38,7 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error { Domain: domain, RequireTLS: options.RequireTLS, Proxy: proxy, + BypassGlobalTLS: options.BypassGlobalTLS, SkipCertValidations: options.SkipCertValidations, RequireBasicAuth: options.RequireBasicAuth, BasicAuthCredentials: options.BasicAuthCredentials, diff --git a/src/mod/info/logger/logger.go b/src/mod/info/logger/logger.go new file mode 100644 index 0000000..bc69ab4 --- /dev/null +++ b/src/mod/info/logger/logger.go @@ -0,0 +1,103 @@ +package logger + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strconv" + "time" +) + +/* + Zoraxy Logger + + This script is designed to make a managed log for the Zoraxy + and replace the ton of log.Println in the system core +*/ + +type Logger struct { + LogToFile bool //Set enable write to file + Prefix string //Prefix for log files + LogFolder string //Folder to store the log file + CurrentLogFile string //Current writing filename + file *os.File +} + +func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, error) { + err := os.MkdirAll(logFolder, 0775) + if err != nil { + return nil, err + } + + thisLogger := Logger{ + LogToFile: logToFile, + Prefix: logFilePrefix, + LogFolder: logFolder, + } + + logFilePath := thisLogger.getLogFilepath() + f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) + if err != nil { + return nil, err + } + thisLogger.CurrentLogFile = logFilePath + thisLogger.file = f + return &thisLogger, nil +} + +func (l *Logger) getLogFilepath() string { + year, month, _ := time.Now().Date() + return filepath.Join(l.LogFolder, l.Prefix+"_"+strconv.Itoa(year)+"-"+strconv.Itoa(int(month))+".log") +} + +// PrintAndLog will log the message to file and print the log to STDOUT +func (l *Logger) PrintAndLog(title string, message string, originalError error) { + go func() { + l.Log(title, message, originalError) + }() + log.Println("[" + title + "] " + message) +} + +// Println is a fast snap-in replacement for log.Println +func (l *Logger) Println(v ...interface{}) { + //Convert the array of interfaces into string + message := fmt.Sprint(v...) + go func() { + l.Log("info", string(message), nil) + }() + log.Println("[INFO] " + string(message)) +} + +func (l *Logger) Log(title string, errorMessage string, originalError error) { + l.ValidateAndUpdateLogFilepath() + if l.LogToFile { + if originalError == nil { + l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [INFO]" + errorMessage + "\n") + } else { + l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [ERROR]" + errorMessage + " " + originalError.Error() + "\n") + } + } + +} + +// Validate if the logging target is still valid (detect any months change) +func (l *Logger) ValidateAndUpdateLogFilepath() { + expectedCurrentLogFilepath := l.getLogFilepath() + if l.CurrentLogFile != expectedCurrentLogFilepath { + //Change of month. Update to a new log file + l.file.Close() + f, err := os.OpenFile(expectedCurrentLogFilepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) + if err != nil { + log.Println("[Logger] Unable to create new log. Logging to file disabled.") + l.LogToFile = false + return + } + l.CurrentLogFile = expectedCurrentLogFilepath + l.file = f + } +} + +func (l *Logger) Close() { + l.file.Close() +} diff --git a/src/mod/info/logviewer/logviewer.go b/src/mod/info/logviewer/logviewer.go new file mode 100644 index 0000000..24f0e19 --- /dev/null +++ b/src/mod/info/logviewer/logviewer.go @@ -0,0 +1,122 @@ +package logviewer + +import ( + "encoding/json" + "errors" + "io/fs" + "net/http" + "os" + "path/filepath" + "strings" + + "imuslab.com/zoraxy/mod/utils" +) + +type ViewerOption struct { + RootFolder string //The root folder to scan for log + Extension string //The extension the root files use, include the . in your ext (e.g. .log) +} + +type Viewer struct { + option *ViewerOption +} + +type LogFile struct { + Title string + Filename string + Fullpath string + Filesize int64 +} + +func NewLogViewer(option *ViewerOption) *Viewer { + return &Viewer{option: option} +} + +/* + Log Request Handlers +*/ +//List all the log files in the log folder. Return in map[string]LogFile format +func (v *Viewer) HandleListLog(w http.ResponseWriter, r *http.Request) { + logFiles := v.ListLogFiles(false) + js, _ := json.Marshal(logFiles) + utils.SendJSONResponse(w, string(js)) +} + +// Read log of a given catergory and filename +// Require GET varaible: file and catergory +func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) { + filename, err := utils.GetPara(r, "file") + if err != nil { + utils.SendErrorResponse(w, "invalid filename given") + return + } + + catergory, err := utils.GetPara(r, "catergory") + if err != nil { + utils.SendErrorResponse(w, "invalid catergory given") + return + } + + content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(catergory)), strings.TrimSpace(filepath.Base(filename))) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendTextResponse(w, content) +} + +/* + Log Access Functions +*/ + +func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile { + result := map[string][]*LogFile{} + filepath.WalkDir(v.option.RootFolder, func(path string, di fs.DirEntry, err error) error { + if filepath.Ext(path) == v.option.Extension { + catergory := filepath.Base(filepath.Dir(path)) + logList, ok := result[catergory] + if !ok { + //this catergory hasn't been scanned before. + logList = []*LogFile{} + } + + fullpath := filepath.ToSlash(path) + if !showFullpath { + fullpath = "" + } + + st, err := os.Stat(path) + if err != nil { + return nil + } + + logList = append(logList, &LogFile{ + Title: strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)), + Filename: filepath.Base(path), + Fullpath: fullpath, + Filesize: st.Size(), + }) + + result[catergory] = logList + } + + return nil + }) + return result +} + +func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) { + logFilepath := filepath.Join(v.option.RootFolder, catergory, filename) + if utils.FileExists(logFilepath) { + //Load it + content, err := os.ReadFile(logFilepath) + if err != nil { + return "", err + } + + return string(content), nil + } else { + return "", errors.New("log file not found") + } +} diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 43c66de..16196a9 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "log" "net/http" "path/filepath" "sort" @@ -25,33 +24,33 @@ func ReverseProxtInit() { inboundPort := 80 if sysdb.KeyExists("settings", "inbound") { sysdb.Read("settings", "inbound", &inboundPort) - log.Println("Serving inbound port ", inboundPort) + SystemWideLogger.Println("Serving inbound port ", inboundPort) } else { - log.Println("Inbound port not set. Using default (80)") + SystemWideLogger.Println("Inbound port not set. Using default (80)") } useTls := false sysdb.Read("settings", "usetls", &useTls) if useTls { - log.Println("TLS mode enabled. Serving proxxy request with TLS") + SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS") } else { - log.Println("TLS mode disabled. Serving proxy request with plain http") + SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http") } forceLatestTLSVersion := false sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion) if forceLatestTLSVersion { - log.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2") + SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2") } else { - log.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0") + SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0") } forceHttpsRedirect := false sysdb.Read("settings", "redirect", &forceHttpsRedirect) if forceHttpsRedirect { - log.Println("Force HTTPS mode enabled") + SystemWideLogger.Println("Force HTTPS mode enabled") } else { - log.Println("Force HTTPS mode disabled") + SystemWideLogger.Println("Force HTTPS mode disabled") } dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{ @@ -67,7 +66,7 @@ func ReverseProxtInit() { WebDirectory: *staticWebServerRoot, }) if err != nil { - log.Println(err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err) return } @@ -78,7 +77,7 @@ func ReverseProxtInit() { for _, conf := range confs { record, err := LoadReverseProxyConfig(conf) if err != nil { - log.Println("Failed to load "+filepath.Base(conf), err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err) return } @@ -92,6 +91,7 @@ func ReverseProxtInit() { MatchingDomain: record.Rootname, Domain: record.ProxyTarget, RequireTLS: record.UseTLS, + BypassGlobalTLS: record.BypassGlobalTLS, SkipCertValidations: record.SkipTlsValidation, RequireBasicAuth: record.RequireBasicAuth, BasicAuthCredentials: record.BasicAuthCredentials, @@ -102,13 +102,14 @@ func ReverseProxtInit() { RootName: record.Rootname, Domain: record.ProxyTarget, RequireTLS: record.UseTLS, + BypassGlobalTLS: record.BypassGlobalTLS, SkipCertValidations: record.SkipTlsValidation, RequireBasicAuth: record.RequireBasicAuth, BasicAuthCredentials: record.BasicAuthCredentials, BasicAuthExceptionRules: record.BasicAuthExceptionRules, }) } else { - log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf)) + SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil) } } @@ -117,7 +118,7 @@ func ReverseProxtInit() { //reverse proxy server in front of this service time.Sleep(300 * time.Millisecond) dynamicProxyRouter.StartProxyService() - log.Println("Dynamic Reverse Proxy service started") + SystemWideLogger.Println("Dynamic Reverse Proxy service started") //Add all proxy services to uptime monitor //Create a uptime monitor service @@ -128,7 +129,7 @@ func ReverseProxtInit() { Interval: 300, //5 minutes MaxRecordsStore: 288, //1 day }) - log.Println("Uptime Monitor background service started") + SystemWideLogger.Println("Uptime Monitor background service started") }() } @@ -180,6 +181,13 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { useTLS := (tls == "true") + bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS") + if bypassGlobalTLS == "" { + bypassGlobalTLS = "false" + } + + useBypassGlobalTLS := bypassGlobalTLS == "true" + stv, _ := utils.PostPara(r, "tlsval") if stv == "" { stv = "false" @@ -240,6 +248,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { RootName: vdir, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -257,6 +266,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { MatchingDomain: subdomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -281,6 +291,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { Rootname: rootname, ProxyTarget: endpoint, UseTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipTlsValidation: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -385,6 +396,10 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, } SaveReverseProxyConfigToFile(&thisProxyConfigRecord) + + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } @@ -417,6 +432,9 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) { uptimeMonitor.CleanRecords() } + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } @@ -751,11 +769,11 @@ func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) { } if useRedirect == "true" { sysdb.Write("settings", "redirect", true) - log.Println("Updating force HTTPS redirection to true") + SystemWideLogger.Println("Updating force HTTPS redirection to true") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true) } else if useRedirect == "false" { sysdb.Write("settings", "redirect", false) - log.Println("Updating force HTTPS redirection to false") + SystemWideLogger.Println("Updating force HTTPS redirection to false") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false) } diff --git a/src/start.go b/src/start.go index 6d210cb..c7da3b7 100644 --- a/src/start.go +++ b/src/start.go @@ -14,6 +14,7 @@ import ( "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/ganserv" "imuslab.com/zoraxy/mod/geodb" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/mdns" "imuslab.com/zoraxy/mod/netstat" "imuslab.com/zoraxy/mod/pathrule" @@ -93,10 +94,17 @@ func startupSequence() { panic(err) } + //Create a system wide logger + l, err := logger.NewLogger("zr", "./log", *logOutputToFile) + if err == nil { + SystemWideLogger = l + } else { + panic(err) + } //Create a netstat buffer netstatBuffers, err = netstat.NewNetStatBuffer(300) if err != nil { - log.Println("Failed to load network statistic info") + SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err) panic(err) } @@ -134,13 +142,13 @@ func startupSequence() { BuildVersion: version, }, "") if err != nil { - log.Println("Unable to startup mDNS service. Disabling mDNS services") + SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services") } else { //Start initial scanning go func() { hosts := mdnsScanner.Scan(30, "") previousmdnsScanResults = hosts - log.Println("mDNS Startup scan completed") + SystemWideLogger.Println("mDNS Startup scan completed") }() //Create a ticker to update mDNS results every 5 minutes @@ -154,7 +162,7 @@ func startupSequence() { case <-ticker.C: hosts := mdnsScanner.Scan(30, "") previousmdnsScanResults = hosts - log.Println("mDNS scan result updated") + SystemWideLogger.Println("mDNS scan result updated") } } }() @@ -171,7 +179,7 @@ func startupSequence() { if usingZtAuthToken == "" { usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey() if err != nil { - log.Println("Failed to load ZeroTier controller API authtoken") + SystemWideLogger.Println("Failed to load ZeroTier controller API authtoken") } } ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{ diff --git a/src/web/components/rproot.html b/src/web/components/rproot.html index 57ababf..a1ed65a 100644 --- a/src/web/components/rproot.html +++ b/src/web/components/rproot.html @@ -112,8 +112,10 @@ isUsingStaticWebServerAsRoot(function(isUsingWebServ){ if (isUsingWebServ){ $(".webservRootDisabled").addClass("disabled"); + $("#useStaticWebServer").parent().checkbox("set checked"); }else{ $(".webservRootDisabled").removeClass("disabled"); + $("#useStaticWebServer").parent().checkbox("set unchecked"); } }) } diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 194ea7e..4e9bfb3 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -47,6 +47,12 @@ +
+
+ + +
+
@@ -123,6 +129,7 @@ var proxyDomain = $("#proxyDomain").val(); var useTLS = $("#reqTls")[0].checked; var skipTLSValidation = $("#skipTLSValidation")[0].checked; + var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked; var requireBasicAuth = $("#requireBasicAuth")[0].checked; if (type === "vdir") { @@ -162,6 +169,7 @@ tls: useTLS, ep: proxyDomain, tlsval: skipTLSValidation, + bypassGlobalTLS: bypassGlobalTLS, bauth: requireBasicAuth, cred: JSON.stringify(credentials), }, diff --git a/src/web/components/uptime.html b/src/web/components/uptime.html index f5492f0..761e4ab 100644 --- a/src/web/components/uptime.html +++ b/src/web/components/uptime.html @@ -86,7 +86,7 @@ let id = value[0].ID; let name = value[0].Name; - let url = value[0].URL; + let url = value[value.length - 1].URL; let protocol = value[0].Protocol; //Generate the status dot @@ -112,6 +112,9 @@ if (thisStatus.StatusCode >= 500 && thisStatus.StatusCode < 600){ //Special type of error, cause by downstream reverse proxy dotType = "error"; + }else if (thisStatus.StatusCode == 401){ + //Unauthorized error + dotType = "error"; }else{ dotType = "offline"; } @@ -141,6 +144,28 @@ currentOnlineStatus = ` Misconfigured`; onlineStatusCss = `color: #f38020;`; reminderEle = `Downstream proxy server is online with misconfigured settings`; + }else if (value[value.length - 1].StatusCode >= 400 && value[value.length - 1].StatusCode <= 405){ + switch(value[value.length - 1].StatusCode){ + case 400: + currentOnlineStatus = ` Bad Request`; + break; + case 401: + currentOnlineStatus = ` Unauthorized`; + break; + case 403: + currentOnlineStatus = ` Forbidden`; + break; + case 404: + currentOnlineStatus = ` Not Found`; + break; + case 405: + currentOnlineStatus = ` Method Not Allowed`; + break; + } + + onlineStatusCss = `color: #f38020;`; + reminderEle = `Target online but not accessible`; + }else{ currentOnlineStatus = ` Offline`; onlineStatusCss = `color: #df484a;`; diff --git a/src/wrappers.go b/src/wrappers.go index f3d6fde..9fd7531 100644 --- a/src/wrappers.go +++ b/src/wrappers.go @@ -19,7 +19,6 @@ package main import ( "encoding/json" "fmt" - "log" "net/http" "strings" "time" @@ -104,7 +103,21 @@ func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) { /* Up Time Monitor */ -//Generate uptime monitor targets from reverse proxy rules + +// Update uptime monitor targets after rules updated +// See https://github.com/tobychui/zoraxy/issues/77 +func UpdateUptimeMonitorTargets() { + if uptimeMonitor != nil { + uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter) + go func() { + uptimeMonitor.ExecuteUptimeCheck() + }() + + SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil) + } +} + +// Generate uptime monitor targets from reverse proxy rules func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target { subds := dp.GetSDProxyEndpointsAsMap() vdirs := dp.GetVDProxyEndpointsAsMap() @@ -263,7 +276,7 @@ func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) { return } - log.Println("[WoL] Sending Wake on LAN magic packet to " + wake) + SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil) err := wakeonlan.WakeTarget(wake) if err != nil { utils.SendErrorResponse(w, err.Error())