mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 23:57:21 +02:00
Added apache compatible logger
- Rewritten the logger to make it more apache log parser friendly - Fixed uptime not updating after upstream change bug - Added SSO page (wip) - Added log viewer
This commit is contained in:
parent
e410b92e34
commit
8239f4cb53
@ -148,7 +148,7 @@ func initAPIs() {
|
|||||||
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)
|
||||||
|
|
||||||
//TCP Proxy
|
//Stream (TCP / UDP) Proxy
|
||||||
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)
|
||||||
@ -229,12 +229,13 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
|
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
|
||||||
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
||||||
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
||||||
|
authRouter.HandleFunc("/api/log/list", LogViewer.HandleListLog)
|
||||||
|
authRouter.HandleFunc("/api/log/read", LogViewer.HandleReadLog)
|
||||||
|
|
||||||
//Debug
|
//Debug
|
||||||
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
|
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
|
||||||
|
|
||||||
//If you got APIs to add, append them here
|
//If you got APIs to add, append them here
|
||||||
// get available docker containers
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ func LoadReverseProxyConfig(configFilepath string) error {
|
|||||||
return errors.New("not supported proxy type")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
src/main.go
34
src/main.go
@ -24,6 +24,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/ganserv"
|
"imuslab.com/zoraxy/mod/ganserv"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logviewer"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
"imuslab.com/zoraxy/mod/pathrule"
|
"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 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 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 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 enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "3.0.8"
|
version = "3.0.8"
|
||||||
nodeUUID = "generic" //System uuid, in uuidv4 format
|
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()
|
bootTime = time.Now().Unix()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -97,6 +97,7 @@ var (
|
|||||||
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
||||||
DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only
|
DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only
|
||||||
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
||||||
|
LogViewer *logviewer.Viewer
|
||||||
)
|
)
|
||||||
|
|
||||||
// Kill signal handler. Do something before the system the core terminate.
|
// Kill signal handler. Do something before the system the core terminate.
|
||||||
@ -111,33 +112,34 @@ func SetupCloseHandler() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ShutdownSeq() {
|
func ShutdownSeq() {
|
||||||
fmt.Println("- Shutting down " + name)
|
SystemWideLogger.Println("Shutting down " + name)
|
||||||
fmt.Println("- Closing GeoDB ")
|
SystemWideLogger.Println("Closing GeoDB ")
|
||||||
geodbStore.Close()
|
geodbStore.Close()
|
||||||
fmt.Println("- Closing Netstats Listener")
|
SystemWideLogger.Println("Closing Netstats Listener")
|
||||||
netstatBuffers.Close()
|
netstatBuffers.Close()
|
||||||
fmt.Println("- Closing Statistic Collector")
|
SystemWideLogger.Println("Closing Statistic Collector")
|
||||||
statisticCollector.Close()
|
statisticCollector.Close()
|
||||||
if mdnsTickerStop != nil {
|
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
|
// Stop the mdns service
|
||||||
mdnsTickerStop <- true
|
mdnsTickerStop <- true
|
||||||
}
|
}
|
||||||
mdnsScanner.Close()
|
mdnsScanner.Close()
|
||||||
fmt.Println("- Shutting down load balancer")
|
SystemWideLogger.Println("Shutting down load balancer")
|
||||||
loadBalancer.Close()
|
loadBalancer.Close()
|
||||||
fmt.Println("- Closing Certificates Auto Renewer")
|
SystemWideLogger.Println("Closing Certificates Auto Renewer")
|
||||||
acmeAutoRenewer.Close()
|
acmeAutoRenewer.Close()
|
||||||
//Remove the tmp folder
|
//Remove the tmp folder
|
||||||
fmt.Println("- Cleaning up tmp files")
|
SystemWideLogger.Println("Cleaning up tmp files")
|
||||||
os.RemoveAll("./tmp")
|
os.RemoveAll("./tmp")
|
||||||
|
|
||||||
fmt.Println("- Closing system wide logger")
|
//Close database
|
||||||
SystemWideLogger.Close()
|
SystemWideLogger.Println("Stopping system database")
|
||||||
|
|
||||||
//Close database, final
|
|
||||||
fmt.Println("- Stopping system database")
|
|
||||||
sysdb.Close()
|
sysdb.Close()
|
||||||
|
|
||||||
|
//Close logger
|
||||||
|
SystemWideLogger.Println("Closing system wide logger")
|
||||||
|
SystemWideLogger.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -154,7 +156,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *enableAutoUpdate {
|
if *enableAutoUpdate {
|
||||||
log.Println("[INFO] Checking required config update")
|
fmt.Println("Checking required config update")
|
||||||
update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(version))
|
update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
db "imuslab.com/zoraxy/mod/database"
|
db "imuslab.com/zoraxy/mod/database"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ type AuthAgent struct {
|
|||||||
SessionStore *sessions.CookieStore
|
SessionStore *sessions.CookieStore
|
||||||
Database *db.Database
|
Database *db.Database
|
||||||
LoginRedirectionHandler func(http.ResponseWriter, *http.Request)
|
LoginRedirectionHandler func(http.ResponseWriter, *http.Request)
|
||||||
|
Logger *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthEndpoints struct {
|
type AuthEndpoints struct {
|
||||||
@ -38,11 +39,11 @@ type AuthEndpoints struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
func NewAuthenticationAgent(sessionName string, key []byte, sysdb *db.Database, allowReg bool, loginRedirectionHandler func(http.ResponseWriter, *http.Request)) *AuthAgent {
|
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)
|
store := sessions.NewCookieStore(key)
|
||||||
err := sysdb.NewTable("auth")
|
err := sysdb.NewTable("auth")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to create auth database. Terminating.")
|
systemLogger.Println("Failed to create auth database. Terminating.")
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,13 +53,14 @@ func NewAuthenticationAgent(sessionName string, key []byte, sysdb *db.Database,
|
|||||||
SessionStore: store,
|
SessionStore: store,
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
LoginRedirectionHandler: loginRedirectionHandler,
|
LoginRedirectionHandler: loginRedirectionHandler,
|
||||||
|
Logger: systemLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return the authAgent
|
//Return the authAgent
|
||||||
return &newAuthAgent
|
return &newAuthAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSessionKey(sysdb *db.Database) (string, error) {
|
func GetSessionKey(sysdb *db.Database, logger *logger.Logger) (string, error) {
|
||||||
sysdb.NewTable("auth")
|
sysdb.NewTable("auth")
|
||||||
sessionKey := ""
|
sessionKey := ""
|
||||||
if !sysdb.KeyExists("auth", "sessionkey") {
|
if !sysdb.KeyExists("auth", "sessionkey") {
|
||||||
@ -66,9 +68,9 @@ func GetSessionKey(sysdb *db.Database) (string, error) {
|
|||||||
rand.Read(key)
|
rand.Read(key)
|
||||||
sessionKey = string(key)
|
sessionKey = string(key)
|
||||||
sysdb.Write("auth", "sessionkey", sessionKey)
|
sysdb.Write("auth", "sessionkey", sessionKey)
|
||||||
log.Println("[Auth] New authentication session key generated")
|
logger.PrintAndLog("auth", "New authentication session key generated", nil)
|
||||||
} else {
|
} 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)
|
err := sysdb.Read("auth", "sessionkey", &sessionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("database read error. Is the database file corrupted?")
|
return "", errors.New("database read error. Is the database file corrupted?")
|
||||||
@ -95,7 +97,7 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
username, err := utils.PostPara(r, "username")
|
username, err := utils.PostPara(r, "username")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Username not defined
|
//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.")
|
utils.SendErrorResponse(w, "Username not defined or empty.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -124,11 +126,11 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.LoginUserByRequest(w, r, username, rememberme)
|
a.LoginUserByRequest(w, r, username, rememberme)
|
||||||
|
|
||||||
//Print the login message to console
|
//Print the login message to console
|
||||||
log.Println(username + " logged in.")
|
a.Logger.PrintAndLog("auth", username+" logged in.", nil)
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
} else {
|
} else {
|
||||||
//Password incorrect
|
//Password incorrect
|
||||||
log.Println(username + " login request rejected: " + rejectionReason)
|
a.Logger.PrintAndLog("auth", username+" login request rejected: "+rejectionReason, nil)
|
||||||
|
|
||||||
utils.SendErrorResponse(w, rejectionReason)
|
utils.SendErrorResponse(w, rejectionReason)
|
||||||
return
|
return
|
||||||
@ -147,7 +149,7 @@ func (a *AuthAgent) ValidateUsernameAndPasswordWithReason(username string, passw
|
|||||||
err := a.Database.Read("auth", "passhash/"+username, &passwordInDB)
|
err := a.Database.Read("auth", "passhash/"+username, &passwordInDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//User not found or db exception
|
//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"
|
return false, "Invalid username or password"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,8 +186,12 @@ func (a *AuthAgent) LoginUserByRequest(w http.ResponseWriter, r *http.Request, u
|
|||||||
// 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) {
|
func (a *AuthAgent) HandleLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
username, err := a.GetUserName(w, r)
|
username, err := a.GetUserName(w, r)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "user not logged in")
|
||||||
|
return
|
||||||
|
}
|
||||||
if username != "" {
|
if username != "" {
|
||||||
log.Println(username + " logged out.")
|
a.Logger.PrintAndLog("auth", username+" logged out", nil)
|
||||||
}
|
}
|
||||||
// Revoke users authentication
|
// Revoke users authentication
|
||||||
err = a.Logout(w, r)
|
err = a.Logout(w, r)
|
||||||
@ -194,7 +200,7 @@ func (a *AuthAgent) HandleLogout(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write([]byte("OK"))
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error {
|
func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error {
|
||||||
@ -291,7 +297,7 @@ func (a *AuthAgent) HandleRegister(w http.ResponseWriter, r *http.Request, callb
|
|||||||
|
|
||||||
//Return to the client with OK
|
//Return to the client with OK
|
||||||
utils.SendOK(w)
|
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.
|
||||||
@ -324,7 +330,7 @@ func (a *AuthAgent) HandleRegisterWithoutEmail(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
//Return to the client with OK
|
//Return to the client with OK
|
||||||
utils.SendOK(w)
|
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
|
||||||
@ -365,7 +371,7 @@ func (a *AuthAgent) HandleUnregister(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
//Return to the client with OK
|
//Return to the client with OK
|
||||||
utils.SendOK(w)
|
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 {
|
func (a *AuthAgent) UnregisterUser(username string) error {
|
||||||
@ -393,7 +399,7 @@ func (a *AuthAgent) GetUserCounts() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if usercount == 0 {
|
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
|
return usercount
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ func NewManagedHTTPRouter(option RouterOption) *RouterDef {
|
|||||||
func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseWriter, *http.Request)) error {
|
func (router *RouterDef) HandleFunc(endpoint string, handler func(http.ResponseWriter, *http.Request)) error {
|
||||||
//Check if the endpoint already registered
|
//Check if the endpoint already registered
|
||||||
if _, exist := router.endpoints[endpoint]; exist {
|
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")
|
return errors.New("endpoint register duplicated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if sep.RequireRateLimit {
|
if sep.RequireRateLimit {
|
||||||
err := h.handleRateLimitRouting(w, r, sep)
|
err := h.handleRateLimitRouting(w, r, sep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
h.Parent.Option.Logger.LogHTTPRequest(r, "host", 429)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,6 +86,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if sep.RequireBasicAuth {
|
if sep.RequireBasicAuth {
|
||||||
err := h.handleBasicAuthRouting(w, r, sep)
|
err := h.handleBasicAuthRouting(w, r, sep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
h.Parent.Option.Logger.LogHTTPRequest(r, "host", 401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,6 +103,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if potentialProxtEndpoint != nil && !potentialProxtEndpoint.Disabled {
|
if potentialProxtEndpoint != nil && !potentialProxtEndpoint.Disabled {
|
||||||
//Missing tailing slash. Redirect to target proxy endpoint
|
//Missing tailing slash. Redirect to target proxy endpoint
|
||||||
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
||||||
|
h.Parent.Option.Logger.LogHTTPRequest(r, "redirect", 307)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
selectedUpstream, err := router.loadBalancer.GetRequestUpstreamTarget(w, r, sep.ActiveOrigins, sep.UseStickySession)
|
selectedUpstream, err := router.loadBalancer.GetRequestUpstreamTarget(w, r, sep.ActiveOrigins, sep.UseStickySession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.ServeFile(w, r, "./web/hosterror.html")
|
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)
|
router.logRequest(r, false, 404, "vdir-http", r.Host)
|
||||||
}
|
}
|
||||||
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
@ -195,7 +195,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
IdleTimeout: 120 * time.Second,
|
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
|
//Create a redirection stop channel
|
||||||
stopChan := make(chan bool)
|
stopChan := make(chan bool)
|
||||||
@ -206,7 +206,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
httpServer.Shutdown(ctx)
|
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
|
//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
|
//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() {
|
go func() {
|
||||||
if err := router.server.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
|
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 {
|
} else {
|
||||||
@ -232,10 +232,9 @@ func (router *Router) StartProxyService() error {
|
|||||||
router.tlsListener = nil
|
router.tlsListener = nil
|
||||||
router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
|
router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
|
||||||
router.Running = true
|
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() {
|
go func() {
|
||||||
router.server.ListenAndServe()
|
router.server.ListenAndServe()
|
||||||
//log.Println("[DynamicProxy] " + err.Error())
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
if selectedUpstream.RequireTLS {
|
if selectedUpstream.RequireTLS {
|
||||||
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL)
|
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{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
||||||
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
||||||
@ -173,15 +173,15 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
if errors.As(err, &dnsError) {
|
if errors.As(err, &dnsError) {
|
||||||
http.ServeFile(w, r, "./web/hosterror.html")
|
http.ServeFile(w, r, "./web/hosterror.html")
|
||||||
log.Println(err.Error())
|
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 {
|
} else {
|
||||||
http.ServeFile(w, r, "./web/rperror.html")
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
log.Println(err.Error())
|
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
|
// 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) {
|
func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, forwardType string, target string) {
|
||||||
if router.Option.StatisticCollector != nil {
|
if router.Option.StatisticCollector != nil {
|
||||||
go func() {
|
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.StatisticCollector.RecordRequest(requestInfo)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
router.Option.Logger.LogHTTPRequest(r, forwardType, statusCode)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
|
"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
"imuslab.com/zoraxy/mod/tlscert"
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ type RouterOption struct {
|
|||||||
StatisticCollector *statistic.Collector //Statistic collector for storing stats on incoming visitors
|
StatisticCollector *statistic.Collector //Statistic collector for storing stats on incoming visitors
|
||||||
WebDirectory string //The static web server directory containing the templates folder
|
WebDirectory string //The static web server directory containing the templates folder
|
||||||
LoadBalancer *loadbalance.RouteManager //Load balancer that handle load balancing of proxy target
|
LoadBalancer *loadbalance.RouteManager //Load balancer that handle load balancing of proxy target
|
||||||
|
Logger *logger.Logger //Logger for reverse proxy requets
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Router Object */
|
/* Router Object */
|
||||||
|
@ -13,29 +13,31 @@ import (
|
|||||||
Zoraxy Logger
|
Zoraxy Logger
|
||||||
|
|
||||||
This script is designed to make a managed log for the Zoraxy
|
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 {
|
type Logger struct {
|
||||||
LogToFile bool //Set enable write to file
|
|
||||||
Prefix string //Prefix for log files
|
Prefix string //Prefix for log files
|
||||||
LogFolder string //Folder to store the log file
|
LogFolder string //Folder to store the log file
|
||||||
CurrentLogFile string //Current writing filename
|
CurrentLogFile string //Current writing filename
|
||||||
|
logger *log.Logger
|
||||||
file *os.File
|
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)
|
err := os.MkdirAll(logFolder, 0775)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
thisLogger := Logger{
|
thisLogger := Logger{
|
||||||
LogToFile: logToFile,
|
|
||||||
Prefix: logFilePrefix,
|
Prefix: logFilePrefix,
|
||||||
LogFolder: logFolder,
|
LogFolder: logFolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create the log file if not exists
|
||||||
logFilePath := thisLogger.getLogFilepath()
|
logFilePath := thisLogger.getLogFilepath()
|
||||||
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -43,9 +45,26 @@ func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger,
|
|||||||
}
|
}
|
||||||
thisLogger.CurrentLogFile = logFilePath
|
thisLogger.CurrentLogFile = logFilePath
|
||||||
thisLogger.file = f
|
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
|
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 {
|
func (l *Logger) getLogFilepath() string {
|
||||||
year, month, _ := time.Now().Date()
|
year, month, _ := time.Now().Date()
|
||||||
return filepath.Join(l.LogFolder, l.Prefix+"_"+strconv.Itoa(year)+"-"+strconv.Itoa(int(month))+".log")
|
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
|
// PrintAndLog will log the message to file and print the log to STDOUT
|
||||||
func (l *Logger) PrintAndLog(title string, message string, originalError error) {
|
func (l *Logger) PrintAndLog(title string, message string, originalError error) {
|
||||||
go func() {
|
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
|
// 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
|
//Convert the array of interfaces into string
|
||||||
message := fmt.Sprint(v...)
|
message := fmt.Sprint(v...)
|
||||||
go func() {
|
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()
|
l.ValidateAndUpdateLogFilepath()
|
||||||
if l.LogToFile {
|
if l.logger == nil || copyToSTDOUT {
|
||||||
|
//Use STDOUT instead of logger
|
||||||
if originalError == nil {
|
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 {
|
} 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)
|
// Validate if the logging target is still valid (detect any months change)
|
||||||
func (l *Logger) ValidateAndUpdateLogFilepath() {
|
func (l *Logger) ValidateAndUpdateLogFilepath() {
|
||||||
|
if l.file == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
expectedCurrentLogFilepath := l.getLogFilepath()
|
expectedCurrentLogFilepath := l.getLogFilepath()
|
||||||
if l.CurrentLogFile != expectedCurrentLogFilepath {
|
if l.CurrentLogFile != expectedCurrentLogFilepath {
|
||||||
//Change of month. Update to a new log file
|
//Change of month. Update to a new log file
|
||||||
l.file.Close()
|
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)
|
f, err := os.OpenFile(expectedCurrentLogFilepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Logger] Unable to create new log. Logging to file disabled.")
|
log.Println("Unable to create new log. Logging is disabled: ", err.Error())
|
||||||
l.LogToFile = false
|
l.logger = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.CurrentLogFile = expectedCurrentLogFilepath
|
l.CurrentLogFile = expectedCurrentLogFilepath
|
||||||
l.file = f
|
l.file = f
|
||||||
|
|
||||||
|
//Start a new logger
|
||||||
|
logger := log.New(f, "", log.Default().Flags())
|
||||||
|
l.logger = logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
32
src/mod/info/logger/trafficlog.go
Normal file
32
src/mod/info/logger/trafficlog.go
Normal file
@ -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)
|
||||||
|
}()
|
||||||
|
}
|
@ -3,6 +3,7 @@ package logviewer
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -51,13 +52,7 @@ func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
catergory, err := utils.GetPara(r, "catergory")
|
content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(filename)))
|
||||||
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 {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -106,8 +101,11 @@ func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) {
|
func (v *Viewer) LoadLogFile(filename string) (string, error) {
|
||||||
logFilepath := filepath.Join(v.option.RootFolder, catergory, filename)
|
filename = filepath.ToSlash(filename)
|
||||||
|
filename = strings.ReplaceAll(filename, "../", "")
|
||||||
|
logFilepath := filepath.Join(v.option.RootFolder, filename)
|
||||||
|
fmt.Println(logFilepath)
|
||||||
if utils.FileExists(logFilepath) {
|
if utils.FileExists(logFilepath) {
|
||||||
//Load it
|
//Load it
|
||||||
content, err := os.ReadFile(logFilepath)
|
content, err := os.ReadFile(logFilepath)
|
||||||
|
@ -39,7 +39,6 @@ type NetStatBuffers struct {
|
|||||||
|
|
||||||
// Get a new network statistic buffers
|
// Get a new network statistic buffers
|
||||||
func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) {
|
func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) {
|
||||||
|
|
||||||
//Flood fill the stats with 0
|
//Flood fill the stats with 0
|
||||||
initialStats := []*FlowStat{}
|
initialStats := []*FlowStat{}
|
||||||
for i := 0; i < recordCount; i++ {
|
for i := 0; i < recordCount; i++ {
|
||||||
|
@ -2,16 +2,23 @@ package uptime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
logModuleName = "uptime-monitor"
|
||||||
|
)
|
||||||
|
|
||||||
type Record struct {
|
type Record struct {
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
ID string
|
ID string
|
||||||
@ -42,6 +49,7 @@ type Config struct {
|
|||||||
Targets []*Target
|
Targets []*Target
|
||||||
Interval int
|
Interval int
|
||||||
MaxRecordsStore int
|
MaxRecordsStore int
|
||||||
|
Logger *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type Monitor struct {
|
type Monitor struct {
|
||||||
@ -64,6 +72,12 @@ func NewUptimeMonitor(config *Config) (*Monitor, error) {
|
|||||||
Config: config,
|
Config: config,
|
||||||
OnlineStatusLog: map[string][]*Record{},
|
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
|
//Start the endpoint listener
|
||||||
ticker := time.NewTicker(time.Duration(config.Interval) * time.Second)
|
ticker := time.NewTicker(time.Duration(config.Interval) * time.Second)
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
@ -77,7 +91,7 @@ func NewUptimeMonitor(config *Config) (*Monitor, error) {
|
|||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
case t := <-ticker.C:
|
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()
|
thisMonitor.ExecuteUptimeCheck()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +105,7 @@ func (m *Monitor) ExecuteUptimeCheck() {
|
|||||||
//For each target to check online, do the following
|
//For each target to check online, do the following
|
||||||
var thisRecord Record
|
var thisRecord Record
|
||||||
if target.Protocol == "http" || target.Protocol == "https" {
|
if target.Protocol == "http" || target.Protocol == "https" {
|
||||||
online, laterncy, statusCode := getWebsiteStatusWithLatency(target.URL)
|
online, laterncy, statusCode := m.getWebsiteStatusWithLatency(target.URL)
|
||||||
thisRecord = Record{
|
thisRecord = Record{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
ID: target.ID,
|
ID: target.ID,
|
||||||
@ -104,7 +118,7 @@ func (m *Monitor) ExecuteUptimeCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Println("Unknown protocol: " + target.Protocol + ". Skipping")
|
m.Config.Logger.PrintAndLog(logModuleName, "Unknown protocol: "+target.Protocol, errors.New("unsupported protocol"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +138,6 @@ func (m *Monitor) ExecuteUptimeCheck() {
|
|||||||
m.OnlineStatusLog[target.ID] = thisRecords
|
m.OnlineStatusLog[target.ID] = thisRecords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Write results to db
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Monitor) AddTargetToMonitor(target *Target) {
|
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
|
// 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)
|
start := time.Now().UnixNano() / int64(time.Millisecond)
|
||||||
statusCode, err := getWebsiteStatus(url)
|
statusCode, err := getWebsiteStatus(url)
|
||||||
end := time.Now().UnixNano() / int64(time.Millisecond)
|
end := time.Now().UnixNano() / int64(time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
m.Config.Logger.PrintAndLog(logModuleName, "Ping upstream timeout. Assume offline", err)
|
||||||
return false, 0, 0
|
return false, 0, 0
|
||||||
} else {
|
} else {
|
||||||
diff := end - start
|
diff := end - start
|
||||||
|
@ -5,13 +5,13 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
"imuslab.com/zoraxy/mod/webserv/filemanager"
|
"imuslab.com/zoraxy/mod/webserv/filemanager"
|
||||||
)
|
)
|
||||||
@ -30,6 +30,7 @@ type WebServerOptions struct {
|
|||||||
EnableDirectoryListing bool //Enable listing of directory
|
EnableDirectoryListing bool //Enable listing of directory
|
||||||
WebRoot string //Folder for stroing the static web folders
|
WebRoot string //Folder for stroing the static web folders
|
||||||
EnableWebDirManager bool //Enable web file manager to handle files in web directory
|
EnableWebDirManager bool //Enable web file manager to handle files in web directory
|
||||||
|
Logger *logger.Logger //System logger
|
||||||
Sysdb *database.Database //Database for storing configs
|
Sysdb *database.Database //Database for storing configs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +46,16 @@ type WebServer struct {
|
|||||||
|
|
||||||
// NewWebServer creates a new WebServer instance. One instance only
|
// NewWebServer creates a new WebServer instance. One instance only
|
||||||
func NewWebServer(options *WebServerOptions) *WebServer {
|
func NewWebServer(options *WebServerOptions) *WebServer {
|
||||||
|
if options.Logger == nil {
|
||||||
|
options.Logger, _ = logger.NewFmtLogger()
|
||||||
|
}
|
||||||
if !utils.FileExists(options.WebRoot) {
|
if !utils.FileExists(options.WebRoot) {
|
||||||
//Web root folder not exists. Create one with default templates
|
//Web root folder not exists. Create one with default templates
|
||||||
os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775)
|
os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775)
|
||||||
os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775)
|
os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775)
|
||||||
indexTemplate, err := templates.ReadFile("templates/index.html")
|
indexTemplate, err := templates.ReadFile("templates/index.html")
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
os.WriteFile(filepath.Join(options.WebRoot, "html", "index.html"), indexTemplate, 0775)
|
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.
|
// ChangePort changes the server's port.
|
||||||
func (ws *WebServer) ChangePort(port string) error {
|
func (ws *WebServer) ChangePort(port string) error {
|
||||||
if IsPortInUse(port) {
|
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 {
|
if ws.isRunning {
|
||||||
@ -119,6 +123,7 @@ func (ws *WebServer) ChangePort(port string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws.option.Logger.PrintAndLog("static-webserv", "Listening port updated to "+port, nil)
|
||||||
ws.option.Sysdb.Write("webserv", "port", port)
|
ws.option.Sysdb.Write("webserv", "port", port)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -141,7 +146,7 @@ func (ws *WebServer) Start() error {
|
|||||||
|
|
||||||
//Check if the port is usable
|
//Check if the port is usable
|
||||||
if IsPortInUse(ws.option.Port) {
|
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
|
//Dispose the old mux and create a new one
|
||||||
@ -159,12 +164,12 @@ func (ws *WebServer) Start() error {
|
|||||||
go func() {
|
go func() {
|
||||||
if err := ws.server.ListenAndServe(); err != nil {
|
if err := ws.server.ListenAndServe(); err != nil {
|
||||||
if err != http.ErrServerClosed {
|
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.isRunning = true
|
||||||
ws.option.Sysdb.Write("webserv", "enabled", true)
|
ws.option.Sysdb.Write("webserv", "enabled", true)
|
||||||
return nil
|
return nil
|
||||||
@ -182,7 +187,7 @@ func (ws *WebServer) Stop() error {
|
|||||||
if err := ws.server.Close(); err != nil {
|
if err := ws.server.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ws.option.Logger.PrintAndLog("static-webserv", "Static Web Server stopped", nil)
|
||||||
ws.isRunning = false
|
ws.isRunning = false
|
||||||
ws.option.Sysdb.Write("webserv", "enabled", false)
|
ws.option.Sysdb.Write("webserv", "enabled", false)
|
||||||
return nil
|
return nil
|
||||||
|
@ -98,9 +98,10 @@ func ReverseProxtInit() {
|
|||||||
WebDirectory: *staticWebServerRoot,
|
WebDirectory: *staticWebServerRoot,
|
||||||
AccessController: accessController,
|
AccessController: accessController,
|
||||||
LoadBalancer: loadBalancer,
|
LoadBalancer: loadBalancer,
|
||||||
|
Logger: SystemWideLogger,
|
||||||
})
|
})
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ func ReverseProxtInit() {
|
|||||||
for _, conf := range confs {
|
for _, conf := range confs {
|
||||||
err := LoadReverseProxyConfig(conf)
|
err := LoadReverseProxyConfig(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
SystemWideLogger.PrintAndLog("proxy-config", "Failed to load config file: "+filepath.Base(conf), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +125,7 @@ func ReverseProxtInit() {
|
|||||||
//Root config not set (new deployment?), use internal static web server as root
|
//Root config not set (new deployment?), use internal static web server as root
|
||||||
defaultRootRouter, err := GetDefaultRootConfig()
|
defaultRootRouter, err := GetDefaultRootConfig()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
|
dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
|
||||||
@ -145,6 +146,7 @@ func ReverseProxtInit() {
|
|||||||
Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter),
|
Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter),
|
||||||
Interval: 300, //5 minutes
|
Interval: 300, //5 minutes
|
||||||
MaxRecordsStore: 288, //1 day
|
MaxRecordsStore: 288, //1 day
|
||||||
|
Logger: SystemWideLogger, //Logger
|
||||||
})
|
})
|
||||||
|
|
||||||
SystemWideLogger.Println("Uptime Monitor background service started")
|
SystemWideLogger.Println("Uptime Monitor background service started")
|
||||||
@ -412,7 +414,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
//Save the config to file
|
//Save the config to file
|
||||||
err = SaveReverseProxyConfig(proxyEndpointCreated)
|
err = SaveReverseProxyConfig(proxyEndpointCreated)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,15 +482,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
proxyRateLimit = 1000
|
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
|
//Load the previous basic auth credentials from current proxy rules
|
||||||
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
|
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -498,11 +491,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
//Generate a new proxyEndpoint from the new config
|
//Generate a new proxyEndpoint from the new config
|
||||||
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
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.BypassGlobalTLS = bypassGlobalTLS
|
||||||
newProxyEndpoint.RequireBasicAuth = requireBasicAuth
|
newProxyEndpoint.RequireBasicAuth = requireBasicAuth
|
||||||
newProxyEndpoint.RequireRateLimit = requireRateLimit
|
newProxyEndpoint.RequireRateLimit = requireRateLimit
|
||||||
@ -521,9 +509,6 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
//Save it to file
|
//Save it to file
|
||||||
SaveReverseProxyConfig(newProxyEndpoint)
|
SaveReverseProxyConfig(newProxyEndpoint)
|
||||||
|
|
||||||
//Update uptime monitor
|
|
||||||
UpdateUptimeMonitorTargets()
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +540,7 @@ func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) {
|
|||||||
newAlias := []string{}
|
newAlias := []string{}
|
||||||
err = json.Unmarshal([]byte(newAliasJSON), &newAlias)
|
err = json.Unmarshal([]byte(newAliasJSON), &newAlias)
|
||||||
if err != nil {
|
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")
|
utils.SendErrorResponse(w, "Invalid alias list given")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -577,7 +562,7 @@ func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = SaveReverseProxyConfig(newProxyEndpoint)
|
err = SaveReverseProxyConfig(newProxyEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "Alias update failed")
|
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)
|
utils.SendOK(w)
|
||||||
@ -881,6 +866,10 @@ func ReverseProxyToggleRuleSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.SendErrorResponse(w, "unable to save updated rule")
|
utils.SendErrorResponse(w, "unable to save updated rule")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update uptime monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/start.go
26
src/start.go
@ -20,6 +20,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/ganserv"
|
"imuslab.com/zoraxy/mod/ganserv"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logviewer"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
"imuslab.com/zoraxy/mod/pathrule"
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
@ -47,6 +48,18 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func startupSequence() {
|
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
|
//Create database
|
||||||
db, err := database.NewDatabase("sys.db", false)
|
db, err := database.NewDatabase("sys.db", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -61,11 +74,11 @@ func startupSequence() {
|
|||||||
os.MkdirAll("./conf/proxy/", 0775)
|
os.MkdirAll("./conf/proxy/", 0775)
|
||||||
|
|
||||||
//Create an auth agent
|
//Create an auth agent
|
||||||
sessionKey, err := auth.GetSessionKey(sysdb)
|
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, 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
|
//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)
|
||||||
})
|
})
|
||||||
@ -76,14 +89,6 @@ func startupSequence() {
|
|||||||
panic(err)
|
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
|
//Create a redirection rule table
|
||||||
db.NewTable("redirect")
|
db.NewTable("redirect")
|
||||||
redirectAllowRegexp := false
|
redirectAllowRegexp := false
|
||||||
@ -134,6 +139,7 @@ func startupSequence() {
|
|||||||
WebRoot: *staticWebServerRoot,
|
WebRoot: *staticWebServerRoot,
|
||||||
EnableDirectoryListing: true,
|
EnableDirectoryListing: true,
|
||||||
EnableWebDirManager: *allowWebFileManager,
|
EnableWebDirManager: *allowWebFileManager,
|
||||||
|
Logger: SystemWideLogger,
|
||||||
})
|
})
|
||||||
//Restore the web server to previous shutdown state
|
//Restore the web server to previous shutdown state
|
||||||
staticWebServer.RestorePreviousState()
|
staticWebServer.RestorePreviousState()
|
||||||
|
@ -109,6 +109,9 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update Uptime Monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +183,9 @@ func ReverseProxyUpstreamUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.SendErrorResponse(w, "Failed to save updated upstream config")
|
utils.SendErrorResponse(w, "Failed to save updated upstream config")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update Uptime Monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,5 +276,8 @@ func ReverseProxyUpstreamDelete(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update uptime monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>Service Expose Proxy</h2>
|
<h2>Single-Sign-On</h2>
|
||||||
<p>Expose your local test-site on the internet with single command</p>
|
<p>Create and manage accounts with Zoraxy!</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<h4>Work In Progress</h4>
|
<h4>Work In Progress</h4>
|
@ -120,7 +120,12 @@
|
|||||||
<!-- Config Tools -->
|
<!-- Config Tools -->
|
||||||
<h3>System Backup & Restore</h3>
|
<h3>System Backup & Restore</h3>
|
||||||
<p>Options related to system backup, migrate and restore.</p>
|
<p>Options related to system backup, migrate and restore.</p>
|
||||||
<button class="ui basic button" onclick="showSideWrapper('snippet/configTools.html');">Open Config Tools</button>
|
<button class="ui basic button" onclick="showSideWrapper('snippet/configTools.html');"><i class="ui green undo icon icon"></i> Open Config Tools</button>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<!-- Log Viewer -->
|
||||||
|
<h3>System Log Viewer</h3>
|
||||||
|
<p>View and download Zoraxy log</p>
|
||||||
|
<button class="ui basic button" onclick="launchToolWithSize('snippet/logview.html', 1024, 768);"><i class="ui blue file icon"></i> Open Log Viewer</button>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<!-- System Information -->
|
<!-- System Information -->
|
||||||
<div id="zoraxyinfo">
|
<div id="zoraxyinfo">
|
||||||
|
@ -55,21 +55,21 @@
|
|||||||
<i class="simplistic exchange icon"></i> Stream Proxy
|
<i class="simplistic exchange icon"></i> Stream Proxy
|
||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Access & Connections</div>
|
<div class="ui divider menudivider">Access & Connections</div>
|
||||||
<a class="item" tag="cert">
|
|
||||||
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
|
||||||
</a>
|
|
||||||
<a class="item" tag="redirectset">
|
<a class="item" tag="redirectset">
|
||||||
<i class="simplistic level up alternate icon"></i> Redirection
|
<i class="simplistic level up alternate icon"></i> Redirection
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="access">
|
<a class="item" tag="access">
|
||||||
<i class="simplistic ban icon"></i> Access Control
|
<i class="simplistic ban icon"></i> Access Control
|
||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Bridging</div>
|
|
||||||
<a class="item" tag="gan">
|
<a class="item" tag="gan">
|
||||||
<i class="simplistic globe icon"></i> Global Area Network
|
<i class="simplistic globe icon"></i> Global Area Network
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="zgrok">
|
<div class="ui divider menudivider">Security</div>
|
||||||
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
<a class="item" tag="cert">
|
||||||
|
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||||
|
</a>
|
||||||
|
<a class="item" tag="sso">
|
||||||
|
<i class="simplistic user circle icon"></i> SSO / Oauth
|
||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Others</div>
|
<div class="ui divider menudivider">Others</div>
|
||||||
<a class="item" tag="webserv">
|
<a class="item" tag="webserv">
|
||||||
@ -120,8 +120,8 @@
|
|||||||
<!-- Global Area Networking -->
|
<!-- Global Area Networking -->
|
||||||
<div id="gan" class="functiontab" target="gan.html"></div>
|
<div id="gan" class="functiontab" target="gan.html"></div>
|
||||||
|
|
||||||
<!-- Service Expose Proxy -->
|
<!-- SSO / Oauth services -->
|
||||||
<div id="zgrok" class="functiontab" target="zgrok.html"></div>
|
<div id="sso" class="functiontab" target="sso.html"></div>
|
||||||
|
|
||||||
<!-- TCP Proxy -->
|
<!-- TCP Proxy -->
|
||||||
<div id="streamproxy" class="functiontab" target="streamprox.html"></div>
|
<div id="streamproxy" class="functiontab" target="streamprox.html"></div>
|
||||||
|
155
src/web/snippet/logview.html
Normal file
155
src/web/snippet/logview.html
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="App">
|
||||||
|
<head>
|
||||||
|
<title>System Logs</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
|
||||||
|
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
|
||||||
|
<script type="text/javascript" src="../script/jquery-3.6.0.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../script/semantic/semantic.min.js"></script>
|
||||||
|
<style>
|
||||||
|
.clickable{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.clickable:hover{
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.logfile{
|
||||||
|
padding-left: 1em !important;
|
||||||
|
position: relative;
|
||||||
|
padding-right: 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loglist{
|
||||||
|
background-color: rgb(250, 250, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile .showing{
|
||||||
|
position: absolute;
|
||||||
|
top: 0.18em;
|
||||||
|
right: 0em;
|
||||||
|
margin-right: -0.4em;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logfile.active .showing{
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logrender{
|
||||||
|
width: 100% !important;
|
||||||
|
height: calc(100% - 1.2em);
|
||||||
|
min-height: calc(90vh - 1.2em) !important;
|
||||||
|
border: 0px solid transparent !important;
|
||||||
|
background-color: #252630;
|
||||||
|
color: white;
|
||||||
|
font-family: monospace;
|
||||||
|
overflow-x: scroll !important;
|
||||||
|
white-space: pre;
|
||||||
|
resize: none;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logrender::selection{
|
||||||
|
background:#3643bb;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<br>
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="ui stackable grid">
|
||||||
|
<div class="four wide column loglist">
|
||||||
|
<h3 class="ui header" style="padding-top: 1em;">
|
||||||
|
<div class="content">
|
||||||
|
Log View
|
||||||
|
<div class="sub header">Check System Log in Real Time</div>
|
||||||
|
</div>
|
||||||
|
</h3>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div id="logList" class="ui accordion">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<small>Notes: Some log files might be huge. Make sure you have checked the log file size before opening</small>
|
||||||
|
</div>
|
||||||
|
<div class="twelve wide column">
|
||||||
|
<textarea id="logrender" spellcheck="false" readonly="true">
|
||||||
|
← Pick a log file from the left menu to start debugging
|
||||||
|
</textarea>
|
||||||
|
<a href="#" onclick="openLogInNewTab();">Open In New Tab</a>
|
||||||
|
<br><br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
var currentOpenedLogURL = "";
|
||||||
|
|
||||||
|
function openLogInNewTab(){
|
||||||
|
if (currentOpenedLogURL != ""){
|
||||||
|
window.open(currentOpenedLogURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openLog(object, catergory, filename){
|
||||||
|
$(".logfile.active").removeClass('active');
|
||||||
|
$(object).addClass("active");
|
||||||
|
currentOpenedLogURL = "/api/log/read?file=" + filename;
|
||||||
|
$.get(currentOpenedLogURL, function(data){
|
||||||
|
if (data.error !== undefined){
|
||||||
|
alert(data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#logrender").val(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initLogList(){
|
||||||
|
$("#logList").html("");
|
||||||
|
$.get("/api/log/list", function(data){
|
||||||
|
//console.log(data);
|
||||||
|
for (let [key, value] of Object.entries(data)) {
|
||||||
|
console.log(key, value);
|
||||||
|
value.reverse(); //Default value was from oldest to newest
|
||||||
|
var fileItemList = "";
|
||||||
|
value.forEach(file => {
|
||||||
|
fileItemList += `<div class="item clickable logfile" onclick="openLog(this, '${key}','${file.Filename}');">
|
||||||
|
<i class="file outline icon"></i>
|
||||||
|
<div class="content">
|
||||||
|
${file.Title} (${formatBytes(file.Filesize)})
|
||||||
|
<div class="showing"><i class="green chevron right icon"></i></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
})
|
||||||
|
$("#logList").append(`<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
${key}
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="ui list">
|
||||||
|
${fileItemList}
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".ui.accordion").accordion();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
initLogList();
|
||||||
|
|
||||||
|
|
||||||
|
function formatBytes(x){
|
||||||
|
var units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
let l = 0, n = parseInt(x, 10) || 0;
|
||||||
|
while(n >= 1024 && ++l){
|
||||||
|
n = n/1024;
|
||||||
|
}
|
||||||
|
return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
@ -75,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<i class="ui blue info circle icon"></i> Round-robin load balancing algorithm will be used for upstreams with same weight. Set weight to 0 for fallback only.
|
<i class="ui blue info circle icon"></i> Weighted random will be used for load-balancing. Set weight to 0 for fallback only.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui tab basic segment" data-tab="newupstream">
|
<div class="ui tab basic segment" data-tab="newupstream">
|
||||||
|
@ -115,7 +115,7 @@ func UpdateUptimeMonitorTargets() {
|
|||||||
uptimeMonitor.ExecuteUptimeCheck()
|
uptimeMonitor.ExecuteUptimeCheck()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil)
|
SystemWideLogger.PrintAndLog("uptime-monitor", "Uptime monitor config updated", nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +125,12 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
|
|||||||
|
|
||||||
UptimeTargets := []*uptime.Target{}
|
UptimeTargets := []*uptime.Target{}
|
||||||
for hostid, target := range hosts {
|
for hostid, target := range hosts {
|
||||||
for _, origin := range target.ActiveOrigins {
|
if target.Disabled {
|
||||||
|
//Skip those proxy rules that is disabled
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isMultipleUpstreams := len(target.ActiveOrigins) > 1
|
||||||
|
for i, origin := range target.ActiveOrigins {
|
||||||
url := "http://" + origin.OriginIpOrDomain
|
url := "http://" + origin.OriginIpOrDomain
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
if origin.RequireTLS {
|
if origin.RequireTLS {
|
||||||
@ -135,9 +139,13 @@ func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Add the root url
|
//Add the root url
|
||||||
|
hostIdAndName := hostid
|
||||||
|
if isMultipleUpstreams {
|
||||||
|
hostIdAndName = hostIdAndName + " (upstream:" + strconv.Itoa(i) + ")"
|
||||||
|
}
|
||||||
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
UptimeTargets = append(UptimeTargets, &uptime.Target{
|
||||||
ID: hostid,
|
ID: hostIdAndName,
|
||||||
Name: hostid,
|
Name: hostIdAndName,
|
||||||
URL: url,
|
URL: url,
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
ProxyType: uptime.ProxyType_Host,
|
ProxyType: uptime.ProxyType_Host,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user