From ccab22de7123e092c1f1a34ed8955d48e9c217f6 Mon Sep 17 00:00:00 2001 From: "PAEPCKE, Michael" Date: Mon, 11 Nov 2024 06:43:26 +0000 Subject: [PATCH] remove: central git log for now, refactor api section --- .attic/httpd-handler.go-git | 279 ++++++++++++++++++++++++++++++++++++ .gitignore | 31 ---- api.go | 110 +------------- go.mod | 2 +- httpd-handler.go | 75 ---------- httpd-srv.go | 1 - srv.go | 115 +++++++++++++++ 7 files changed, 397 insertions(+), 216 deletions(-) create mode 100644 .attic/httpd-handler.go-git delete mode 100644 .gitignore create mode 100644 srv.go diff --git a/.attic/httpd-handler.go-git b/.attic/httpd-handler.go-git new file mode 100644 index 0000000..677b08a --- /dev/null +++ b/.attic/httpd-handler.go-git @@ -0,0 +1,279 @@ +package opnborg + +import ( + "bytes" + "net/http" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/alecthomas/chroma/v2/quick" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + "paepcke.de/npad/compress" +) + +const ( + _ua = "User-Agent" + _utf8 = "text/html;charset=utf-8" + _txt = "text/plain" + _ctype = "Content-Type" + _title = "title" + _app = " [ -= OPNBORG =- ] " +) + +// getForceHandler +func getForceHandler() http.Handler { + h := func(r http.ResponseWriter, q *http.Request) { + update <- true + r = headHTML(r) + _, _ = r.Write([]byte(_forceRedirect)) + } + return http.HandlerFunc(h) +} + +// getFavIconHandler +func getFavIconHandler() http.Handler { + h := func(r http.ResponseWriter, q *http.Request) { + r.Header().Set("Content-Type", "image/png") + r.Header().Set("Content-Length", strconv.Itoa(len(_favicon))) + _, _ = r.Write(_favicon) + } + return http.HandlerFunc(h) +} + +// getIndexHandler +func getIndexHandler() http.Handler { + h := func(r http.ResponseWriter, q *http.Request) { + r = headHTML(r) + switch q.Method { + case "GET": + compress.WriteTransportCompressedPage(getStartHTML(), r, q, true) + default: + inf := "Error: Method Not Allowed (405) [" + q.Method + "]" + http.Error(r, inf, http.StatusMethodNotAllowed) + } + } + return http.HandlerFunc(h) +} + +// getGitHandler +func getGitHandler() http.Handler { + h := func(r http.ResponseWriter, q *http.Request) { + r = headHTML(r) + switch q.Method { + case "GET": + compress.WriteTransportCompressedPage(getGitHTML(), r, q, true) + default: + inf := "Error: Method Not Allowed (405) [" + q.Method + "]" + http.Error(r, inf, http.StatusMethodNotAllowed) + } + } + return http.HandlerFunc(h) +} + +// getStartHTML is the root page +func getStartHTML() string { + var s strings.Builder + s.WriteString(_htmlStart) + s.WriteString(_head) + s.WriteString(_bodyStart) + s.WriteString(_bodyHead) + s.WriteString(borg) + s.WriteString(getNavi()) + s.WriteString(getHive()) + s.WriteString(_gitLogLink) + s.WriteString(getPKG()) + s.WriteString(_bodySemVer) + s.WriteString(_bodyFooter) + s.WriteString(_bodyEnd) + s.WriteString(_htmlEnd) + return s.String() +} + +// headHTML +func headHTML(r http.ResponseWriter) http.ResponseWriter { + r.Header().Set(_ctype, _utf8) + r.Header().Set(_title, _app) + return r +} + +// addSecurityHeader ... +func addSecurityHeader(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // w.Header().Set("Cross-Origin-Embedder-Policy", "require-corp") + // w.Header().Set("Cross-Origin-Opener-Policy", "same-origin") + next.ServeHTTP(w, req) + }) +} + +// getPKG ... +func getPKG() string { + if len(syncPKG) < 5 { + return _empty + } + var s strings.Builder + s.WriteString("

BorgSYNC
[ Module:Package-Sync:Active ]
\n") + s.WriteString("

") + s.WriteString("
") + s.WriteString(strings.ReplaceAll(strings.ReplaceAll(syncPKG, ",", " / "), "os-", "")) + s.WriteString("
") + s.WriteString("
\n") + return s.String() +} + +// getHive +func getHive() string { + var s strings.Builder + s.WriteString("


") + hiveMutex.Lock() // snapshot (freeze) state + for _, grp := range tg { + if grp.Img { + s.WriteString("\""
") + } else { + s.WriteString("" + grp.Name + "
") + } + s.WriteString(" ") + s.WriteString(_lf) + for _, srv := range grp.Member { + s.WriteString(" ") + s.WriteString(_lf) + } + s.WriteString("
") + for _, line := range hive { + if strings.Contains(line, srv) { + s.WriteString(line) + break + } + } + s.WriteString("
") + s.WriteString("
") + s.WriteString(_lf) + } + hiveMutex.Unlock() + s.WriteString(_lf) + s.WriteString("BorgBACKUP
Module:Monitor:Backup:Active
[ Automatic check every ") + s.WriteString(sleep) + s.WriteString(" seconds ]

" + _lf) + s.WriteString(_forceButton + "

" + _lf) + return s.String() +} + +// getNavi provides the central top navigation links +func getNavi() string { + var s strings.Builder + if prometheusWebUI != "" { + s.WriteString(" ") + } + if grafanaWebUI != "" { + s.WriteString(" ") + } + if grafanaFreeBSD != "" { + s.WriteString(" ") + } + if grafanaHAProxy != "" { + s.WriteString(" ") + } + if grafanaUnpoller != "" { + s.WriteString(" ") + } + if unifiWebUI != "" { + s.WriteString(" ") + } + if wazuhWebUI != "" { + s.WriteString(" ") + } + return s.String() +} + +// getGitLog ... +func getGitHTML() string { + cgit := false + if cgit { + // native c lib git log + var buf bytes.Buffer + cmd := exec.Command("git", "log", "-c", "--since=14days") + gitLog, err := cmd.CombinedOutput() + if err != nil { + _ = quick.Highlight(&buf, err.Error(), "diff", "html", "github") + return buf.String() + } + _ = quick.Highlight(&buf, string(gitLog), "diff", "html", "github") + return buf.String() + } + + // open git repo + repo, err := git.PlainOpen(_currentDir) + if err != nil { + return err.Error() + } + + // identify repo head + ref, err := repo.Head() + if err != nil { + return err.Error() + } + + // Fetch Log + now := time.Now() + since := time.Now().AddDate(0, -14, 0) + objIter, err := repo.Log(&git.LogOptions{ + From: ref.Hash(), + Since: &since, + Until: &now, + }) + if err != nil { + return err.Error() + } + var s strings.Builder + s.WriteString("
")
+	_ = objIter.ForEach(func(c *object.Commit) error {
+		s.WriteString(_linefeed)
+		s.WriteString(_linefeed)
+		obj, _ := repo.CommitObject(c.Hash)
+		s.WriteString(obj.String())
+		s.WriteString(_linefeed)
+		return nil
+	})
+	s.WriteString("
") + return s.String() +} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index eafcd5b..0000000 --- a/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work -go.work.sum - -# env file -.env - -# backup test opn dir -192* - -# dev internal archive -.attic diff --git a/api.go b/api.go index 1f5a51d..50b8951 100644 --- a/api.go +++ b/api.go @@ -1,10 +1,8 @@ package opnborg import ( - "strings" "sync" "sync/atomic" - "time" ) // global exported consts @@ -96,111 +94,7 @@ var hive []string var hiveMutex sync.Mutex var update = make(chan bool, 1) -// Start Application +// Start Application Server func Start(config *OPNCall) error { - // init - var err error - - // spin up Log/Display Engine - display.Add(1) - - // spin up internal log / display engine - go startLog(config) - - // spin up internal webserver - state := "[DISABLED]" - if config.Httpd.Enable { - go startWeb(config) - state = "[ENABLED]" - } - displayChan <- []byte("[SERVICE][HTTPD]" + state) - - // spin up internal rsyslog server - state = "[DISABLED]" - if config.RSysLog.Enable { - go startRSysLog(config) - state = "[ENABLED]" - } - displayChan <- []byte("[SERVICE][RSYSLOG]" + state) - - // setup hive - servers := strings.Split(config.Targets, ",") - for _, server := range servers { - status := _na + " Member: " + server + " Version: n/a Last Seen: n/a
" - hive = append(hive, status) - } - - // startup app version & state, sleep panic gate - suffix := "[CLI-ONE-TIME-PASS-MODE]" - if config.Daemon { - suffix = "[DAEMON-MODE][SLEEP:" + sleep + " SECONDS]" - } - displayChan <- []byte("[STARTING][" + _app + "][" + SemVer + "]" + suffix) - - // spin up timer - go func() { - time.Sleep(time.Duration(config.Sleep) * time.Second) - update <- true - }() - - // main loop - for { - - // fetch target configuration from master server - if config.Sync.Enable { - config.Sync.validConf = true - config, err = readMasterConf(config) - if err != nil { - config.Sync.validConf = false - displayChan <- []byte("[ERROR][UNABLE-TO-READ-MASTER-CONFIG]" + err.Error()) - } - } - - // reset global (atomic) git worktree state tracker - if config.Git { - config.dirty.Store(false) - } - - // spinup individual worker for every server - if config.Debug { - displayChan <- []byte("[STARTING][BACKUP]") - } - for id, server := range servers { - wg.Add(1) - go actionSrv(server, config, id, &wg) - } - - // wait till all worker done - wg.Wait() - - // check files into local git repo - if config.dirty.Load() { - if config.Git { - if err := gitCheckIn(config); err != nil { - displayChan <- []byte("[GIT][REPO][CHECKIN][FAIL]") - return err - } - displayChan <- []byte("[CHANGES-DETECTED][GIT][REPO][CHECKIN][FINISH]") - } - displayChan <- []byte("[CHANGES-DETECTED][UPDATES-DONE][FINISH]") - } - - // finish - if config.Debug { - displayChan <- []byte("[FINISH][BACKUP][ALL]") - } - - // exit if not in daemon mode - if !config.Daemon { - close(displayChan) - display.Wait() - return nil - } - - // set loop wait - select { - case <-update: - break - } - } + return srv(config) } diff --git a/go.mod b/go.mod index fc45f4d..17e4fd4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module paepcke.de/opnborg -go 1.21 +go 1.22 require ( github.com/alecthomas/chroma/v2 v2.14.0 diff --git a/httpd-handler.go b/httpd-handler.go index 677b08a..fe97c79 100644 --- a/httpd-handler.go +++ b/httpd-handler.go @@ -1,16 +1,10 @@ package opnborg import ( - "bytes" "net/http" - "os/exec" "strconv" "strings" - "time" - "github.com/alecthomas/chroma/v2/quick" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" "paepcke.de/npad/compress" ) @@ -58,21 +52,6 @@ func getIndexHandler() http.Handler { return http.HandlerFunc(h) } -// getGitHandler -func getGitHandler() http.Handler { - h := func(r http.ResponseWriter, q *http.Request) { - r = headHTML(r) - switch q.Method { - case "GET": - compress.WriteTransportCompressedPage(getGitHTML(), r, q, true) - default: - inf := "Error: Method Not Allowed (405) [" + q.Method + "]" - http.Error(r, inf, http.StatusMethodNotAllowed) - } - } - return http.HandlerFunc(h) -} - // getStartHTML is the root page func getStartHTML() string { var s strings.Builder @@ -83,7 +62,6 @@ func getStartHTML() string { s.WriteString(borg) s.WriteString(getNavi()) s.WriteString(getHive()) - s.WriteString(_gitLogLink) s.WriteString(getPKG()) s.WriteString(_bodySemVer) s.WriteString(_bodyFooter) @@ -224,56 +202,3 @@ func getNavi() string { } return s.String() } - -// getGitLog ... -func getGitHTML() string { - cgit := false - if cgit { - // native c lib git log - var buf bytes.Buffer - cmd := exec.Command("git", "log", "-c", "--since=14days") - gitLog, err := cmd.CombinedOutput() - if err != nil { - _ = quick.Highlight(&buf, err.Error(), "diff", "html", "github") - return buf.String() - } - _ = quick.Highlight(&buf, string(gitLog), "diff", "html", "github") - return buf.String() - } - - // open git repo - repo, err := git.PlainOpen(_currentDir) - if err != nil { - return err.Error() - } - - // identify repo head - ref, err := repo.Head() - if err != nil { - return err.Error() - } - - // Fetch Log - now := time.Now() - since := time.Now().AddDate(0, -14, 0) - objIter, err := repo.Log(&git.LogOptions{ - From: ref.Hash(), - Since: &since, - Until: &now, - }) - if err != nil { - return err.Error() - } - var s strings.Builder - s.WriteString("
")
-	_ = objIter.ForEach(func(c *object.Commit) error {
-		s.WriteString(_linefeed)
-		s.WriteString(_linefeed)
-		obj, _ := repo.CommitObject(c.Hash)
-		s.WriteString(obj.String())
-		s.WriteString(_linefeed)
-		return nil
-	})
-	s.WriteString("
") - return s.String() -} diff --git a/httpd-srv.go b/httpd-srv.go index a253961..3642940 100644 --- a/httpd-srv.go +++ b/httpd-srv.go @@ -33,7 +33,6 @@ func startWeb(config *OPNCall) { // handler mux.Handle("/", addSecurityHeader(getIndexHandler())) - mux.Handle("/gitlog/", addSecurityHeader(getGitHandler())) mux.Handle("/files/", addSecurityHeader(http.StripPrefix("/files/", http.FileServer(http.Dir(config.Path))))) mux.Handle("/force", getForceHandler()) mux.Handle("/favicon.ico", getFavIconHandler()) diff --git a/srv.go b/srv.go new file mode 100644 index 0000000..d59a8c2 --- /dev/null +++ b/srv.go @@ -0,0 +1,115 @@ +package opnborg + +import ( + "strings" + "time" +) + +// Start Server Application +func srv(config *OPNCall) error { + // init + var err error + + // spin up Log/Display Engine + display.Add(1) + + // spin up internal log / display engine + go startLog(config) + + // spin up internal webserver + state := "[DISABLED]" + if config.Httpd.Enable { + go startWeb(config) + state = "[ENABLED]" + } + displayChan <- []byte("[SERVICE][HTTPD]" + state) + + // spin up internal rsyslog server + state = "[DISABLED]" + if config.RSysLog.Enable { + go startRSysLog(config) + state = "[ENABLED]" + } + displayChan <- []byte("[SERVICE][RSYSLOG]" + state) + + // setup hive + servers := strings.Split(config.Targets, ",") + for _, server := range servers { + status := _na + " Member: " + server + " Version: n/a Last Seen: n/a
" + hive = append(hive, status) + } + + // startup app version & state, sleep panic gate + suffix := "[CLI-ONE-TIME-PASS-MODE]" + if config.Daemon { + suffix = "[DAEMON-MODE][SLEEP:" + sleep + " SECONDS]" + } + displayChan <- []byte("[STARTING][" + _app + "][" + SemVer + "]" + suffix) + + // spin up timer + go func() { + time.Sleep(time.Duration(config.Sleep) * time.Second) + update <- true + }() + + // main loop + for { + + // fetch target configuration from master server + if config.Sync.Enable { + config.Sync.validConf = true + config, err = readMasterConf(config) + if err != nil { + config.Sync.validConf = false + displayChan <- []byte("[ERROR][UNABLE-TO-READ-MASTER-CONFIG]" + err.Error()) + } + } + + // reset global (atomic) git worktree state tracker + if config.Git { + config.dirty.Store(false) + } + + // spinup individual worker for every server + if config.Debug { + displayChan <- []byte("[STARTING][BACKUP]") + } + for id, server := range servers { + wg.Add(1) + go actionSrv(server, config, id, &wg) + } + + // wait till all worker done + wg.Wait() + + // check files into local git repo + if config.dirty.Load() { + if config.Git { + if err := gitCheckIn(config); err != nil { + displayChan <- []byte("[GIT][REPO][CHECKIN][FAIL]") + return err + } + displayChan <- []byte("[CHANGES-DETECTED][GIT][REPO][CHECKIN][FINISH]") + } + displayChan <- []byte("[CHANGES-DETECTED][UPDATES-DONE][FINISH]") + } + + // finish + if config.Debug { + displayChan <- []byte("[FINISH][BACKUP][ALL]") + } + + // exit if not in daemon mode + if !config.Daemon { + close(displayChan) + display.Wait() + return nil + } + + // set loop wait + select { + case <-update: + break + } + } +}