diff --git a/src/api.go b/src/api.go index 57282a8..471c0b0 100644 --- a/src/api.go +++ b/src/api.go @@ -148,7 +148,7 @@ func initAPIs() { authRouter.HandleFunc("/api/gan/members/authorize", ganManager.HandleMemberAuthorization) authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete) - //TCP Proxy + //Stream (TCP / UDP) Proxy authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig) authRouter.HandleFunc("/api/streamprox/config/edit", streamProxyManager.HandleEditProxyConfigs) authRouter.HandleFunc("/api/streamprox/config/list", streamProxyManager.HandleListConfigs) @@ -229,12 +229,13 @@ func initAPIs() { 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 - // get available docker containers } diff --git a/src/config.go b/src/config.go index 14c6ea9..16954da 100644 --- a/src/config.go +++ b/src/config.go @@ -80,7 +80,7 @@ func LoadReverseProxyConfig(configFilepath string) error { return errors.New("not supported proxy type") } - SystemWideLogger.PrintAndLog("Proxy", thisConfigEndpoint.RootOrMatchingDomain+" -> "+loadbalance.GetUpstreamsAsString(thisConfigEndpoint.ActiveOrigins)+" routing rule loaded", nil) + SystemWideLogger.PrintAndLog("proxy-config", thisConfigEndpoint.RootOrMatchingDomain+" -> "+loadbalance.GetUpstreamsAsString(thisConfigEndpoint.ActiveOrigins)+" routing rule loaded", nil) return nil } diff --git a/src/main.go b/src/main.go index 0cf1dd3..1bda2c8 100644 --- a/src/main.go +++ b/src/main.go @@ -24,6 +24,7 @@ import ( "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" @@ -52,14 +53,13 @@ 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 enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected") var ( name = "Zoraxy" version = "3.0.8" nodeUUID = "generic" //System uuid, in uuidv4 format - development = true //Set this to false to use embedded web fs + development = false //Set this to false to use embedded web fs bootTime = time.Now().Unix() /* @@ -97,6 +97,7 @@ var ( 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. @@ -111,33 +112,34 @@ func SetupCloseHandler() { } func ShutdownSeq() { - fmt.Println("- Shutting down " + name) - fmt.Println("- Closing GeoDB ") + SystemWideLogger.Println("Shutting down " + name) + SystemWideLogger.Println("Closing GeoDB ") geodbStore.Close() - fmt.Println("- Closing Netstats Listener") + SystemWideLogger.Println("Closing Netstats Listener") netstatBuffers.Close() - fmt.Println("- Closing Statistic Collector") + SystemWideLogger.Println("Closing Statistic Collector") statisticCollector.Close() if mdnsTickerStop != nil { - fmt.Println("- Stopping mDNS Discoverer (might take a few minutes)") + SystemWideLogger.Println("Stopping mDNS Discoverer (might take a few minutes)") // Stop the mdns service mdnsTickerStop <- true } mdnsScanner.Close() - fmt.Println("- Shutting down load balancer") + SystemWideLogger.Println("Shutting down load balancer") loadBalancer.Close() - fmt.Println("- Closing Certificates Auto Renewer") + SystemWideLogger.Println("Closing Certificates Auto Renewer") acmeAutoRenewer.Close() //Remove the tmp folder - fmt.Println("- Cleaning up tmp files") + SystemWideLogger.Println("Cleaning up tmp files") os.RemoveAll("./tmp") - fmt.Println("- Closing system wide logger") - SystemWideLogger.Close() - - //Close database, final - fmt.Println("- Stopping system database") + //Close database + SystemWideLogger.Println("Stopping system database") sysdb.Close() + + //Close logger + SystemWideLogger.Println("Closing system wide logger") + SystemWideLogger.Close() } func main() { @@ -154,7 +156,7 @@ func main() { } if *enableAutoUpdate { - log.Println("[INFO] Checking required config update") + fmt.Println("Checking required config update") update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(version)) } diff --git a/src/mod/auth/auth.go b/src/mod/auth/auth.go index 46f42f9..725a839 100644 --- a/src/mod/auth/auth.go +++ b/src/mod/auth/auth.go @@ -14,10 +14,10 @@ import ( "strings" "encoding/hex" - "log" "github.com/gorilla/sessions" db "imuslab.com/zoraxy/mod/database" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/utils" ) @@ -27,6 +27,7 @@ type AuthAgent struct { SessionStore *sessions.CookieStore Database *db.Database LoginRedirectionHandler func(http.ResponseWriter, *http.Request) + Logger *logger.Logger } type AuthEndpoints struct { @@ -37,12 +38,12 @@ type AuthEndpoints struct { Autologin string } -//Constructor -func NewAuthenticationAgent(sessionName string, key []byte, sysdb *db.Database, allowReg bool, loginRedirectionHandler func(http.ResponseWriter, *http.Request)) *AuthAgent { +// Constructor +func NewAuthenticationAgent(sessionName string, key []byte, sysdb *db.Database, allowReg bool, systemLogger *logger.Logger, loginRedirectionHandler func(http.ResponseWriter, *http.Request)) *AuthAgent { store := sessions.NewCookieStore(key) err := sysdb.NewTable("auth") if err != nil { - log.Println("Failed to create auth database. Terminating.") + systemLogger.Println("Failed to create auth database. Terminating.") panic(err) } @@ -52,13 +53,14 @@ func NewAuthenticationAgent(sessionName string, key []byte, sysdb *db.Database, SessionStore: store, Database: sysdb, LoginRedirectionHandler: loginRedirectionHandler, + Logger: systemLogger, } //Return the authAgent return &newAuthAgent } -func GetSessionKey(sysdb *db.Database) (string, error) { +func GetSessionKey(sysdb *db.Database, logger *logger.Logger) (string, error) { sysdb.NewTable("auth") sessionKey := "" if !sysdb.KeyExists("auth", "sessionkey") { @@ -66,9 +68,9 @@ func GetSessionKey(sysdb *db.Database) (string, error) { rand.Read(key) sessionKey = string(key) sysdb.Write("auth", "sessionkey", sessionKey) - log.Println("[Auth] New authentication session key generated") + logger.PrintAndLog("auth", "New authentication session key generated", nil) } else { - log.Println("[Auth] Authentication session key loaded from database") + logger.PrintAndLog("auth", "Authentication session key loaded from database", nil) err := sysdb.Read("auth", "sessionkey", &sessionKey) if err != nil { return "", errors.New("database read error. Is the database file corrupted?") @@ -77,7 +79,7 @@ func GetSessionKey(sysdb *db.Database) (string, error) { return sessionKey, nil } -//This function will handle an http request and redirect to the given login address if not logged in +// This function will handle an http request and redirect to the given login address if not logged in func (a *AuthAgent) HandleCheckAuth(w http.ResponseWriter, r *http.Request, handler func(http.ResponseWriter, *http.Request)) { if a.CheckAuth(r) { //User already logged in @@ -88,14 +90,14 @@ func (a *AuthAgent) HandleCheckAuth(w http.ResponseWriter, r *http.Request, hand } } -//Handle login request, require POST username and password +// Handle login request, require POST username and password func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) { //Get username from request using POST mode username, err := utils.PostPara(r, "username") if err != nil { //Username not defined - log.Println("[Auth] " + r.RemoteAddr + " trying to login with username: " + username) + a.Logger.PrintAndLog("auth", r.RemoteAddr+" trying to login with username: "+username, nil) utils.SendErrorResponse(w, "Username not defined or empty.") return } @@ -124,11 +126,11 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) { a.LoginUserByRequest(w, r, username, rememberme) //Print the login message to console - log.Println(username + " logged in.") + a.Logger.PrintAndLog("auth", username+" logged in.", nil) utils.SendOK(w) } else { //Password incorrect - log.Println(username + " login request rejected: " + rejectionReason) + a.Logger.PrintAndLog("auth", username+" login request rejected: "+rejectionReason, nil) utils.SendErrorResponse(w, rejectionReason) return @@ -140,14 +142,14 @@ func (a *AuthAgent) ValidateUsernameAndPassword(username string, password string return succ } -//validate the username and password, return reasons if the auth failed +// validate the username and password, return reasons if the auth failed func (a *AuthAgent) ValidateUsernameAndPasswordWithReason(username string, password string) (bool, string) { hashedPassword := Hash(password) var passwordInDB string err := a.Database.Read("auth", "passhash/"+username, &passwordInDB) if err != nil { //User not found or db exception - log.Println("[Auth] " + username + " login with incorrect password") + a.Logger.PrintAndLog("auth", username+" login with incorrect password", nil) return false, "Invalid username or password" } @@ -158,7 +160,7 @@ func (a *AuthAgent) ValidateUsernameAndPasswordWithReason(username string, passw } } -//Login the user by creating a valid session for this user +// Login the user by creating a valid session for this user func (a *AuthAgent) LoginUserByRequest(w http.ResponseWriter, r *http.Request, username string, rememberme bool) { session, _ := a.SessionStore.Get(r, a.SessionName) @@ -181,11 +183,15 @@ func (a *AuthAgent) LoginUserByRequest(w http.ResponseWriter, r *http.Request, u session.Save(r, w) } -//Handle logout, reply OK after logged out. WILL NOT DO REDIRECTION +// Handle logout, reply OK after logged out. WILL NOT DO REDIRECTION func (a *AuthAgent) HandleLogout(w http.ResponseWriter, r *http.Request) { username, err := a.GetUserName(w, r) + if err != nil { + utils.SendErrorResponse(w, "user not logged in") + return + } if username != "" { - log.Println(username + " logged out.") + a.Logger.PrintAndLog("auth", username+" logged out", nil) } // Revoke users authentication err = a.Logout(w, r) @@ -194,7 +200,7 @@ func (a *AuthAgent) HandleLogout(w http.ResponseWriter, r *http.Request) { return } - w.Write([]byte("OK")) + utils.SendOK(w) } func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error { @@ -208,7 +214,7 @@ func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error { return nil } -//Get the current session username from request +// Get the current session username from request func (a *AuthAgent) GetUserName(w http.ResponseWriter, r *http.Request) (string, error) { if a.CheckAuth(r) { //This user has logged in. @@ -220,7 +226,7 @@ func (a *AuthAgent) GetUserName(w http.ResponseWriter, r *http.Request) (string, } } -//Get the current session user email from request +// Get the current session user email from request func (a *AuthAgent) GetUserEmail(w http.ResponseWriter, r *http.Request) (string, error) { if a.CheckAuth(r) { //This user has logged in. @@ -239,7 +245,7 @@ func (a *AuthAgent) GetUserEmail(w http.ResponseWriter, r *http.Request) (string } } -//Check if the user has logged in, return true / false in JSON +// Check if the user has logged in, return true / false in JSON func (a *AuthAgent) CheckLogin(w http.ResponseWriter, r *http.Request) { if a.CheckAuth(r) { utils.SendJSONResponse(w, "true") @@ -248,7 +254,7 @@ func (a *AuthAgent) CheckLogin(w http.ResponseWriter, r *http.Request) { } } -//Handle new user register. Require POST username, password, group. +// Handle new user register. Require POST username, password, group. func (a *AuthAgent) HandleRegister(w http.ResponseWriter, r *http.Request, callback func(string, string)) { //Get username from request newusername, err := utils.PostPara(r, "username") @@ -291,10 +297,10 @@ func (a *AuthAgent) HandleRegister(w http.ResponseWriter, r *http.Request, callb //Return to the client with OK utils.SendOK(w) - log.Println("[Auth] New user " + newusername + " added to system.") + a.Logger.PrintAndLog("auth", "New user "+newusername+" added to system.", nil) } -//Handle new user register without confirmation email. Require POST username, password, group. +// Handle new user register without confirmation email. Require POST username, password, group. func (a *AuthAgent) HandleRegisterWithoutEmail(w http.ResponseWriter, r *http.Request, callback func(string, string)) { //Get username from request newusername, err := utils.PostPara(r, "username") @@ -324,10 +330,10 @@ func (a *AuthAgent) HandleRegisterWithoutEmail(w http.ResponseWriter, r *http.Re //Return to the client with OK utils.SendOK(w) - log.Println("[Auth] Admin account created: " + newusername) + a.Logger.PrintAndLog("auth", "Admin account created: "+newusername, nil) } -//Check authentication from request header's session value +// Check authentication from request header's session value func (a *AuthAgent) CheckAuth(r *http.Request) bool { session, err := a.SessionStore.Get(r, a.SessionName) if err != nil { @@ -340,8 +346,8 @@ func (a *AuthAgent) CheckAuth(r *http.Request) bool { return true } -//Handle de-register of users. Require POST username. -//THIS FUNCTION WILL NOT CHECK FOR PERMISSION. PLEASE USE WITH PERMISSION HANDLER +// Handle de-register of users. Require POST username. +// THIS FUNCTION WILL NOT CHECK FOR PERMISSION. PLEASE USE WITH PERMISSION HANDLER func (a *AuthAgent) HandleUnregister(w http.ResponseWriter, r *http.Request) { //Check if the user is logged in if !a.CheckAuth(r) { @@ -365,7 +371,7 @@ func (a *AuthAgent) HandleUnregister(w http.ResponseWriter, r *http.Request) { //Return to the client with OK utils.SendOK(w) - log.Println("[Auth] User " + username + " has been removed from the system.") + a.Logger.PrintAndLog("auth", "User "+username+" has been removed from the system", nil) } func (a *AuthAgent) UnregisterUser(username string) error { @@ -381,7 +387,7 @@ func (a *AuthAgent) UnregisterUser(username string) error { return nil } -//Get the number of users in the system +// Get the number of users in the system func (a *AuthAgent) GetUserCounts() int { entries, _ := a.Database.ListTable("auth") usercount := 0 @@ -393,12 +399,12 @@ func (a *AuthAgent) GetUserCounts() int { } if usercount == 0 { - log.Println("There are no user in the database.") + a.Logger.PrintAndLog("auth", "There are no user in the database", nil) } return usercount } -//List all username within the system +// List all username within the system func (a *AuthAgent) ListUsers() []string { entries, _ := a.Database.ListTable("auth") results := []string{} @@ -411,7 +417,7 @@ func (a *AuthAgent) ListUsers() []string { return results } -//Check if the given username exists +// Check if the given username exists func (a *AuthAgent) UserExists(username string) bool { userpasswordhash := "" err := a.Database.Read("auth", "passhash/"+username, &userpasswordhash) @@ -421,7 +427,7 @@ func (a *AuthAgent) UserExists(username string) bool { return true } -//Update the session expire time given the request header. +// Update the session expire time given the request header. func (a *AuthAgent) UpdateSessionExpireTime(w http.ResponseWriter, r *http.Request) bool { session, _ := a.SessionStore.Get(r, a.SessionName) if session.Values["authenticated"].(bool) { @@ -446,7 +452,7 @@ func (a *AuthAgent) UpdateSessionExpireTime(w http.ResponseWriter, r *http.Reque } } -//Create user account +// Create user account func (a *AuthAgent) CreateUserAccount(newusername string, password string, email string) error { //Check user already exists if a.UserExists(newusername) { @@ -470,7 +476,7 @@ func (a *AuthAgent) CreateUserAccount(newusername string, password string, email return nil } -//Hash the given raw string into sha512 hash +// Hash the given raw string into sha512 hash func Hash(raw string) string { h := sha512.New() h.Write([]byte(raw)) diff --git a/src/mod/auth/router.go b/src/mod/auth/router.go index 546cae0..8fca7b4 100644 --- a/src/mod/auth/router.go +++ b/src/mod/auth/router.go @@ -2,7 +2,7 @@ package auth import ( "errors" - "log" + "fmt" "net/http" ) @@ -28,7 +28,7 @@ func NewManagedHTTPRouter(option RouterOption) *RouterDef { func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseWriter, *http.Request)) error { //Check if the endpoint already registered if _, exist := router.endpoints[endpoint]; exist { - log.Println("WARNING! Duplicated registering of web endpoint: " + endpoint) + fmt.Println("WARNING! Duplicated registering of web endpoint: " + endpoint) return errors.New("endpoint register duplicated") } diff --git a/src/mod/dynamicproxy/Server.go b/src/mod/dynamicproxy/Server.go index d5924d0..f4b74fe 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -77,6 +77,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if sep.RequireRateLimit { err := h.handleRateLimitRouting(w, r, sep) if err != nil { + h.Parent.Option.Logger.LogHTTPRequest(r, "host", 429) return } } @@ -85,6 +86,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if sep.RequireBasicAuth { err := h.handleBasicAuthRouting(w, r, sep) if err != nil { + h.Parent.Option.Logger.LogHTTPRequest(r, "host", 401) return } } @@ -101,6 +103,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if potentialProxtEndpoint != nil && !potentialProxtEndpoint.Disabled { //Missing tailing slash. Redirect to target proxy endpoint http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect) + h.Parent.Option.Logger.LogHTTPRequest(r, "redirect", 307) return } } diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 40ff4e3..4f6e6a8 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -154,7 +154,7 @@ func (router *Router) StartProxyService() error { selectedUpstream, err := router.loadBalancer.GetRequestUpstreamTarget(w, r, sep.ActiveOrigins, sep.UseStickySession) if err != nil { http.ServeFile(w, r, "./web/hosterror.html") - log.Println(err.Error()) + router.Option.Logger.PrintAndLog("dprouter", "failed to get upstream for hostname", err) router.logRequest(r, false, 404, "vdir-http", r.Host) } selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ @@ -195,7 +195,7 @@ func (router *Router) StartProxyService() error { IdleTimeout: 120 * time.Second, } - log.Println("Starting HTTP-to-HTTPS redirector (port 80)") + router.Option.Logger.PrintAndLog("dprouter", "Starting HTTP-to-HTTPS redirector (port 80)", nil) //Create a redirection stop channel stopChan := make(chan bool) @@ -206,7 +206,7 @@ func (router *Router) StartProxyService() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() httpServer.Shutdown(ctx) - log.Println("HTTP to HTTPS redirection listener stopped") + router.Option.Logger.PrintAndLog("dprouter", "HTTP to HTTPS redirection listener stopped", nil) }() //Start the http server that listens to port 80 and redirect to 443 @@ -221,10 +221,10 @@ func (router *Router) StartProxyService() error { } //Start the TLS server - log.Println("Reverse proxy service started in the background (TLS mode)") + router.Option.Logger.PrintAndLog("dprouter", "Reverse proxy service started in the background (TLS mode)", nil) go func() { if err := router.server.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed { - log.Fatalf("Could not start proxy server: %v\n", err) + router.Option.Logger.PrintAndLog("dprouter", "Could not start proxy server", err) } }() } else { @@ -232,10 +232,9 @@ func (router *Router) StartProxyService() error { router.tlsListener = nil router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux} router.Running = true - log.Println("Reverse proxy service started in the background (Plain HTTP mode)") + router.Option.Logger.PrintAndLog("dprouter", "Reverse proxy service started in the background (Plain HTTP mode)", nil) go func() { router.server.ListenAndServe() - //log.Println("[DynamicProxy] " + err.Error()) }() } diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go index 0bc4bb4..389fb03 100644 --- a/src/mod/dynamicproxy/proxyRequestHandler.go +++ b/src/mod/dynamicproxy/proxyRequestHandler.go @@ -136,7 +136,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe if selectedUpstream.RequireTLS { u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL) } - h.Parent.logRequest(r, true, 101, "subdomain-websocket", selectedUpstream.OriginIpOrDomain) + h.Parent.logRequest(r, true, 101, "host-websocket", selectedUpstream.OriginIpOrDomain) wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{ SkipTLSValidation: selectedUpstream.SkipCertValidations, SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck, @@ -173,15 +173,15 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe if errors.As(err, &dnsError) { http.ServeFile(w, r, "./web/hosterror.html") log.Println(err.Error()) - h.Parent.logRequest(r, false, 404, "subdomain-http", r.URL.Hostname()) + h.Parent.logRequest(r, false, 404, "host-http", r.URL.Hostname()) } else { http.ServeFile(w, r, "./web/rperror.html") log.Println(err.Error()) - h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname()) + h.Parent.logRequest(r, false, 521, "host-http", r.URL.Hostname()) } } - h.Parent.logRequest(r, true, 200, "subdomain-http", r.URL.Hostname()) + h.Parent.logRequest(r, true, 200, "host-http", r.URL.Hostname()) } // Handle vdir type request @@ -249,6 +249,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe } +// This logger collect data for the statistical analysis. For log to file logger, check the Logger and LogHTTPRequest handler func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, forwardType string, target string) { if router.Option.StatisticCollector != nil { go func() { @@ -266,4 +267,5 @@ func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, for router.Option.StatisticCollector.RecordRequest(requestInfo) }() } + router.Option.Logger.LogHTTPRequest(r, forwardType, statusCode) } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 908fc70..2effa6c 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -12,6 +12,7 @@ import ( "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/geodb" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/statistic" "imuslab.com/zoraxy/mod/tlscert" ) @@ -43,6 +44,7 @@ type RouterOption struct { StatisticCollector *statistic.Collector //Statistic collector for storing stats on incoming visitors WebDirectory string //The static web server directory containing the templates folder LoadBalancer *loadbalance.RouteManager //Load balancer that handle load balancing of proxy target + Logger *logger.Logger //Logger for reverse proxy requets } /* Router Object */ diff --git a/src/mod/info/logger/logger.go b/src/mod/info/logger/logger.go index bc69ab4..d71f97e 100644 --- a/src/mod/info/logger/logger.go +++ b/src/mod/info/logger/logger.go @@ -13,29 +13,31 @@ import ( 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 + and replace the ton of log.Println in the system core. + The core logger is based in golang's build-in log package */ 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 + logger *log.Logger file *os.File } -func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, error) { +// Create a new logger that log to files +func NewLogger(logFilePrefix string, logFolder string) (*Logger, error) { err := os.MkdirAll(logFolder, 0775) if err != nil { return nil, err } thisLogger := Logger{ - LogToFile: logToFile, Prefix: logFilePrefix, LogFolder: logFolder, } + //Create the log file if not exists logFilePath := thisLogger.getLogFilepath() f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { @@ -43,9 +45,26 @@ func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, } thisLogger.CurrentLogFile = logFilePath thisLogger.file = f + + //Start the logger + logger := log.New(f, "", log.Flags()&^(log.Ldate|log.Ltime)) + logger.SetFlags(0) + logger.SetOutput(f) + thisLogger.logger = logger return &thisLogger, nil } +// Create a fmt logger that only log to STDOUT +func NewFmtLogger() (*Logger, error) { + return &Logger{ + Prefix: "", + LogFolder: "", + CurrentLogFile: "", + logger: nil, + file: nil, + }, 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") @@ -54,9 +73,8 @@ func (l *Logger) getLogFilepath() string { // 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) + l.Log(title, message, originalError, true) }() - log.Println("[" + title + "] " + message) } // Println is a fast snap-in replacement for log.Println @@ -64,18 +82,26 @@ 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) + l.Log("internal", string(message), nil, true) }() - log.Println("[INFO] " + string(message)) } -func (l *Logger) Log(title string, errorMessage string, originalError error) { +func (l *Logger) Log(title string, errorMessage string, originalError error, copyToSTDOUT bool) { l.ValidateAndUpdateLogFilepath() - if l.LogToFile { + if l.logger == nil || copyToSTDOUT { + //Use STDOUT instead of logger if originalError == nil { - l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [INFO]" + errorMessage + "\n") + fmt.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [" + title + "] [system:info] " + errorMessage) } else { - l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [ERROR]" + errorMessage + " " + originalError.Error() + "\n") + fmt.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [" + title + "] [system:error] " + errorMessage + ": " + originalError.Error()) + } + } + + if l.logger != nil { + if originalError == nil { + l.logger.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [" + title + "] [system:info] " + errorMessage) + } else { + l.logger.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [" + title + "] [system:error] " + errorMessage + ": " + originalError.Error()) } } @@ -83,18 +109,28 @@ func (l *Logger) Log(title string, errorMessage string, originalError error) { // Validate if the logging target is still valid (detect any months change) func (l *Logger) ValidateAndUpdateLogFilepath() { + if l.file == nil { + return + } expectedCurrentLogFilepath := l.getLogFilepath() if l.CurrentLogFile != expectedCurrentLogFilepath { //Change of month. Update to a new log file l.file.Close() + l.file = nil + + //Create a new log file 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 + log.Println("Unable to create new log. Logging is disabled: ", err.Error()) + l.logger = nil return } l.CurrentLogFile = expectedCurrentLogFilepath l.file = f + + //Start a new logger + logger := log.New(f, "", log.Default().Flags()) + l.logger = logger } } diff --git a/src/mod/info/logger/trafficlog.go b/src/mod/info/logger/trafficlog.go new file mode 100644 index 0000000..839a698 --- /dev/null +++ b/src/mod/info/logger/trafficlog.go @@ -0,0 +1,32 @@ +package logger + +/* + Traffic Log + + This script log the traffic of HTTP requests + +*/ +import ( + "net/http" + "strconv" + "time" + + "imuslab.com/zoraxy/mod/netutils" +) + +// Log HTTP request. Note that this must run in go routine to prevent any blocking +// in reverse proxy router +func (l *Logger) LogHTTPRequest(r *http.Request, reqclass string, statusCode int) { + go func() { + l.ValidateAndUpdateLogFilepath() + if l.logger == nil || l.file == nil { + //logger is not initiated. Do not log http request + return + } + clientIP := netutils.GetRequesterIP(r) + requestURI := r.RequestURI + statusCodeString := strconv.Itoa(statusCode) + //fmt.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [router:" + reqclass + "] [client " + clientIP + "] " + r.Method + " " + requestURI + " " + statusCodeString) + l.logger.Println("[" + time.Now().Format("2006-01-02 15:04:05.000000") + "] [router:" + reqclass + "] [origin:" + r.URL.Hostname() + "] [client " + clientIP + "] " + r.Method + " " + requestURI + " " + statusCodeString) + }() +} diff --git a/src/mod/info/logviewer/logviewer.go b/src/mod/info/logviewer/logviewer.go index 24f0e19..adaa6fb 100644 --- a/src/mod/info/logviewer/logviewer.go +++ b/src/mod/info/logviewer/logviewer.go @@ -3,6 +3,7 @@ package logviewer import ( "encoding/json" "errors" + "fmt" "io/fs" "net/http" "os" @@ -51,13 +52,7 @@ func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) { 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))) + content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(filename))) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -106,8 +101,11 @@ func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile { return result } -func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) { - logFilepath := filepath.Join(v.option.RootFolder, catergory, filename) +func (v *Viewer) LoadLogFile(filename string) (string, error) { + filename = filepath.ToSlash(filename) + filename = strings.ReplaceAll(filename, "../", "") + logFilepath := filepath.Join(v.option.RootFolder, filename) + fmt.Println(logFilepath) if utils.FileExists(logFilepath) { //Load it content, err := os.ReadFile(logFilepath) diff --git a/src/mod/netstat/netstat.go b/src/mod/netstat/netstat.go index 55dc6e3..51147e6 100644 --- a/src/mod/netstat/netstat.go +++ b/src/mod/netstat/netstat.go @@ -39,7 +39,6 @@ type NetStatBuffers struct { // Get a new network statistic buffers func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) { - //Flood fill the stats with 0 initialStats := []*FlowStat{} for i := 0; i < recordCount; i++ { diff --git a/src/mod/uptime/uptime.go b/src/mod/uptime/uptime.go index e9953b5..1d01814 100644 --- a/src/mod/uptime/uptime.go +++ b/src/mod/uptime/uptime.go @@ -2,16 +2,23 @@ package uptime import ( "encoding/json" + "errors" "log" "net/http" "net/http/cookiejar" + "strconv" "strings" "time" "golang.org/x/net/publicsuffix" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/utils" ) +const ( + logModuleName = "uptime-monitor" +) + type Record struct { Timestamp int64 ID string @@ -42,6 +49,7 @@ type Config struct { Targets []*Target Interval int MaxRecordsStore int + Logger *logger.Logger } type Monitor struct { @@ -64,6 +72,12 @@ func NewUptimeMonitor(config *Config) (*Monitor, error) { Config: config, OnlineStatusLog: map[string][]*Record{}, } + + if config.Logger == nil { + //Use default fmt to log if logger is not provided + config.Logger, _ = logger.NewFmtLogger() + } + //Start the endpoint listener ticker := time.NewTicker(time.Duration(config.Interval) * time.Second) done := make(chan bool) @@ -77,7 +91,7 @@ func NewUptimeMonitor(config *Config) (*Monitor, error) { case <-done: return case t := <-ticker.C: - log.Println("Uptime updated - ", t.Unix()) + thisMonitor.Config.Logger.PrintAndLog(logModuleName, "Uptime updated - "+strconv.Itoa(int(t.Unix())), nil) thisMonitor.ExecuteUptimeCheck() } } @@ -91,7 +105,7 @@ func (m *Monitor) ExecuteUptimeCheck() { //For each target to check online, do the following var thisRecord Record if target.Protocol == "http" || target.Protocol == "https" { - online, laterncy, statusCode := getWebsiteStatusWithLatency(target.URL) + online, laterncy, statusCode := m.getWebsiteStatusWithLatency(target.URL) thisRecord = Record{ Timestamp: time.Now().Unix(), ID: target.ID, @@ -104,7 +118,7 @@ func (m *Monitor) ExecuteUptimeCheck() { } } else { - log.Println("Unknown protocol: " + target.Protocol + ". Skipping") + m.Config.Logger.PrintAndLog(logModuleName, "Unknown protocol: "+target.Protocol, errors.New("unsupported protocol")) continue } @@ -124,8 +138,6 @@ func (m *Monitor) ExecuteUptimeCheck() { m.OnlineStatusLog[target.ID] = thisRecords } } - - //TODO: Write results to db } func (m *Monitor) AddTargetToMonitor(target *Target) { @@ -201,12 +213,12 @@ func (m *Monitor) HandleUptimeLogRead(w http.ResponseWriter, r *http.Request) { */ // Get website stauts with latency given URL, return is conn succ and its latency and status code -func getWebsiteStatusWithLatency(url string) (bool, int64, int) { +func (m *Monitor) getWebsiteStatusWithLatency(url string) (bool, int64, int) { start := time.Now().UnixNano() / int64(time.Millisecond) statusCode, err := getWebsiteStatus(url) end := time.Now().UnixNano() / int64(time.Millisecond) if err != nil { - log.Println(err.Error()) + m.Config.Logger.PrintAndLog(logModuleName, "Ping upstream timeout. Assume offline", err) return false, 0, 0 } else { diff := end - start diff --git a/src/mod/webserv/webserv.go b/src/mod/webserv/webserv.go index d516501..efb8dc9 100644 --- a/src/mod/webserv/webserv.go +++ b/src/mod/webserv/webserv.go @@ -5,13 +5,13 @@ import ( _ "embed" "errors" "fmt" - "log" "net/http" "os" "path/filepath" "sync" "imuslab.com/zoraxy/mod/database" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/webserv/filemanager" ) @@ -30,6 +30,7 @@ type WebServerOptions struct { EnableDirectoryListing bool //Enable listing of directory WebRoot string //Folder for stroing the static web folders EnableWebDirManager bool //Enable web file manager to handle files in web directory + Logger *logger.Logger //System logger Sysdb *database.Database //Database for storing configs } @@ -45,13 +46,16 @@ type WebServer struct { // NewWebServer creates a new WebServer instance. One instance only func NewWebServer(options *WebServerOptions) *WebServer { + if options.Logger == nil { + options.Logger, _ = logger.NewFmtLogger() + } if !utils.FileExists(options.WebRoot) { //Web root folder not exists. Create one with default templates os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775) os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775) indexTemplate, err := templates.ReadFile("templates/index.html") if err != nil { - log.Println("Failed to read static wev server template file: ", err.Error()) + options.Logger.PrintAndLog("static-webserv", "Failed to read static wev server template file: ", err) } else { os.WriteFile(filepath.Join(options.WebRoot, "html", "index.html"), indexTemplate, 0775) } @@ -102,7 +106,7 @@ func (ws *WebServer) RestorePreviousState() { // ChangePort changes the server's port. func (ws *WebServer) ChangePort(port string) error { if IsPortInUse(port) { - return errors.New("Selected port is used by another process") + return errors.New("selected port is used by another process") } if ws.isRunning { @@ -119,6 +123,7 @@ func (ws *WebServer) ChangePort(port string) error { return err } + ws.option.Logger.PrintAndLog("static-webserv", "Listening port updated to "+port, nil) ws.option.Sysdb.Write("webserv", "port", port) return nil @@ -141,7 +146,7 @@ func (ws *WebServer) Start() error { //Check if the port is usable if IsPortInUse(ws.option.Port) { - return errors.New("Port already in use or access denied by host OS") + return errors.New("port already in use or access denied by host OS") } //Dispose the old mux and create a new one @@ -159,12 +164,12 @@ func (ws *WebServer) Start() error { go func() { if err := ws.server.ListenAndServe(); err != nil { if err != http.ErrServerClosed { - fmt.Printf("Web server error: %v\n", err) + ws.option.Logger.PrintAndLog("static-webserv", "Web server failed to start", err) } } }() - log.Println("Static Web Server started. Listeing on :" + ws.option.Port) + ws.option.Logger.PrintAndLog("static-webserv", "Static Web Server started. Listeing on :"+ws.option.Port, nil) ws.isRunning = true ws.option.Sysdb.Write("webserv", "enabled", true) return nil @@ -182,7 +187,7 @@ func (ws *WebServer) Stop() error { if err := ws.server.Close(); err != nil { return err } - + ws.option.Logger.PrintAndLog("static-webserv", "Static Web Server stopped", nil) ws.isRunning = false ws.option.Sysdb.Write("webserv", "enabled", false) return nil diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 1163942..2e5c1d7 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -98,9 +98,10 @@ func ReverseProxtInit() { WebDirectory: *staticWebServerRoot, AccessController: accessController, LoadBalancer: loadBalancer, + Logger: SystemWideLogger, }) if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err) + SystemWideLogger.PrintAndLog("proxy-config", "Unable to create dynamic proxy router", err) return } @@ -115,7 +116,7 @@ func ReverseProxtInit() { for _, conf := range confs { err := LoadReverseProxyConfig(conf) if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err) + SystemWideLogger.PrintAndLog("proxy-config", "Failed to load config file: "+filepath.Base(conf), err) return } } @@ -124,7 +125,7 @@ func ReverseProxtInit() { //Root config not set (new deployment?), use internal static web server as root defaultRootRouter, err := GetDefaultRootConfig() if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err) + SystemWideLogger.PrintAndLog("proxy-config", "Failed to generate default root routing", err) return } dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter) @@ -143,8 +144,9 @@ func ReverseProxtInit() { //This must be done in go routine to prevent blocking on system startup uptimeMonitor, _ = uptime.NewUptimeMonitor(&uptime.Config{ Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter), - Interval: 300, //5 minutes - MaxRecordsStore: 288, //1 day + Interval: 300, //5 minutes + MaxRecordsStore: 288, //1 day + Logger: SystemWideLogger, //Logger }) SystemWideLogger.Println("Uptime Monitor background service started") @@ -412,7 +414,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { //Save the config to file err = SaveReverseProxyConfig(proxyEndpointCreated) if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err) + SystemWideLogger.PrintAndLog("proxy-config", "Unable to save new proxy rule to file", err) return } @@ -480,15 +482,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { proxyRateLimit = 1000 } - // Bypass WebSocket Origin Check - /* - strbpwsorg, _ := utils.PostPara(r, "bpwsorg") - if strbpwsorg == "" { - strbpwsorg = "false" - } - bypassWebsocketOriginCheck := (strbpwsorg == "true") - */ - //Load the previous basic auth credentials from current proxy rules targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain) if err != nil { @@ -498,11 +491,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { //Generate a new proxyEndpoint from the new config newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry) - //TODO: Move these into dedicated module - //newProxyEndpoint.Domain = endpoint - //newProxyEndpoint.RequireTLS = useTLS - //newProxyEndpoint.SkipCertValidations = skipTlsValidation - //newProxyEndpoint.SkipWebSocketOriginCheck = bypassWebsocketOriginCheck newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS newProxyEndpoint.RequireBasicAuth = requireBasicAuth newProxyEndpoint.RequireRateLimit = requireRateLimit @@ -521,9 +509,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { //Save it to file SaveReverseProxyConfig(newProxyEndpoint) - //Update uptime monitor - UpdateUptimeMonitorTargets() - utils.SendOK(w) } @@ -555,7 +540,7 @@ func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) { newAlias := []string{} err = json.Unmarshal([]byte(newAliasJSON), &newAlias) if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Unable to parse new alias list", err) + SystemWideLogger.PrintAndLog("proxy-config", "Unable to parse new alias list", err) utils.SendErrorResponse(w, "Invalid alias list given") return } @@ -577,7 +562,7 @@ func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) { err = SaveReverseProxyConfig(newProxyEndpoint) if err != nil { utils.SendErrorResponse(w, "Alias update failed") - SystemWideLogger.PrintAndLog("Proxy", "Unable to save alias update", err) + SystemWideLogger.PrintAndLog("proxy-config", "Unable to save alias update", err) } utils.SendOK(w) @@ -881,6 +866,10 @@ func ReverseProxyToggleRuleSet(w http.ResponseWriter, r *http.Request) { utils.SendErrorResponse(w, "unable to save updated rule") return } + + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } diff --git a/src/start.go b/src/start.go index 73da080..c77794e 100644 --- a/src/start.go +++ b/src/start.go @@ -20,6 +20,7 @@ import ( "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" @@ -47,6 +48,18 @@ var ( ) func startupSequence() { + //Start a system wide logger and log viewer + l, err := logger.NewLogger("zr", "./log") + if err == nil { + SystemWideLogger = l + } else { + panic(err) + } + LogViewer = logviewer.NewLogViewer(&logviewer.ViewerOption{ + RootFolder: "./log", + Extension: ".log", + }) + //Create database db, err := database.NewDatabase("sys.db", false) if err != nil { @@ -61,11 +74,11 @@ func startupSequence() { os.MkdirAll("./conf/proxy/", 0775) //Create an auth agent - sessionKey, err := auth.GetSessionKey(sysdb) + sessionKey, err := auth.GetSessionKey(sysdb, SystemWideLogger) if err != nil { log.Fatal(err) } - authAgent = auth.NewAuthenticationAgent(name, []byte(sessionKey), sysdb, true, func(w http.ResponseWriter, r *http.Request) { + authAgent = auth.NewAuthenticationAgent(name, []byte(sessionKey), sysdb, true, SystemWideLogger, func(w http.ResponseWriter, r *http.Request) { //Not logged in. Redirecting to login page http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect) }) @@ -76,14 +89,6 @@ 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 redirection rule table db.NewTable("redirect") redirectAllowRegexp := false @@ -134,6 +139,7 @@ func startupSequence() { WebRoot: *staticWebServerRoot, EnableDirectoryListing: true, EnableWebDirManager: *allowWebFileManager, + Logger: SystemWideLogger, }) //Restore the web server to previous shutdown state staticWebServer.RestorePreviousState() diff --git a/src/upstreams.go b/src/upstreams.go index 0f964cc..3ab2eab 100644 --- a/src/upstreams.go +++ b/src/upstreams.go @@ -109,6 +109,9 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) { return } + //Update Uptime Monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } @@ -180,6 +183,9 @@ func ReverseProxyUpstreamUpdate(w http.ResponseWriter, r *http.Request) { utils.SendErrorResponse(w, "Failed to save updated upstream config") return } + + //Update Uptime Monitor + UpdateUptimeMonitorTargets() utils.SendOK(w) } @@ -270,5 +276,8 @@ func ReverseProxyUpstreamDelete(w http.ResponseWriter, r *http.Request) { return } + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } diff --git a/src/web/components/zgrok.html b/src/web/components/sso.html similarity index 75% rename from src/web/components/zgrok.html rename to src/web/components/sso.html index 86200de..4a14f41 100644 --- a/src/web/components/zgrok.html +++ b/src/web/components/sso.html @@ -1,7 +1,7 @@
Expose your local test-site on the internet with single command
+Create and manage accounts with Zoraxy!