From 2a3eee646cbec1599800781e2a9de355fcce16dc Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Wed, 26 Feb 2025 13:26:00 +0100 Subject: [PATCH 01/17] raised version to v0.2.1 --- app/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app.go b/app/app.go index 6bec9c4..cd2e222 100644 --- a/app/app.go +++ b/app/app.go @@ -13,7 +13,7 @@ type Application struct { func new() *Application { app = &Application{} app.Name = "Patchouli" - app.Version = "0.2.0" + app.Version = "0.2.1" app.Author = "Philipp Speck " app.Description = "Patch Management Planner" From bc15a6004980dbf8bcee2728ba99b999c5f89bb3 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Thu, 27 Feb 2025 08:29:43 +0100 Subject: [PATCH 02/17] added interval setting to machines --- app/handler/config/edit.go | 7 ++++--- app/handler/dashboard/list.go | 11 +++++++++-- app/handler/machine/edit.go | 8 ++++++++ app/handler/machine/list.go | 10 +++++++++- app/store/boltdb/boltdb.go | 15 +++++++++++++++ app/structs/machines.go | 1 + app/views/machine/edit.html | 4 ++++ app/views/machine/list.html | 2 ++ 8 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/handler/config/edit.go b/app/handler/config/edit.go index 271c50a..e96afb6 100644 --- a/app/handler/config/edit.go +++ b/app/handler/config/edit.go @@ -3,7 +3,6 @@ package config import ( "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" - "github.com/typomedia/patchouli/app/structs" "golang.org/x/crypto/bcrypt" ) @@ -15,8 +14,10 @@ func Edit(c *fiber.Ctx) error { return err } - var config structs.Config - db.Get("main", &config, "config") + config, err := db.GetConfig() + if err != nil { + return err + } if config.Smtp.Password != "" { passwordHash, err := bcrypt.GenerateFromPassword([]byte(config.Smtp.Password), bcrypt.DefaultCost) if err != nil { diff --git a/app/handler/dashboard/list.go b/app/handler/dashboard/list.go index 173dbb6..1d253bc 100644 --- a/app/handler/dashboard/list.go +++ b/app/handler/dashboard/list.go @@ -60,7 +60,6 @@ func List(c *fiber.Ctx) error { defer db.Close() - interval := config.General.Interval for i := range Machines { currentDate := time.Now() @@ -72,11 +71,19 @@ func List(c *fiber.Ctx) error { Machines[i].Update.Date = helper.UnixToDateString(Machines[i].Update.Date) - date, err := time.Parse("2006-01-02", Machines[i].Update.Date) + date, err := time.Parse(time.DateOnly, Machines[i].Update.Date) if err != nil { log.Error(err) } + var interval int + // check if machine has custom interval + if Machines[i].Interval != 0 { + interval = Machines[i].Interval + } else { + interval = config.General.Interval + } + Machines[i].Days = int(currentDate.Sub(date).Hours() / 24) if Machines[i].Days > interval { Machines[i].Status = "danger" diff --git a/app/handler/machine/edit.go b/app/handler/machine/edit.go index 1dd5680..e3139cf 100644 --- a/app/handler/machine/edit.go +++ b/app/handler/machine/edit.go @@ -23,6 +23,14 @@ func Edit(c *fiber.Ctx) error { log.Error(err) } + var config structs.Config + config, err = db.GetConfig() + if err != nil { + return err + } + if machine.Interval == 0 { + machine.Interval = config.General.Interval + } defer db.Close() return c.Render("app/views/machine/edit", fiber.Map{ diff --git a/app/handler/machine/list.go b/app/handler/machine/list.go index a0f1812..4d1c978 100644 --- a/app/handler/machine/list.go +++ b/app/handler/machine/list.go @@ -11,8 +11,13 @@ import ( func List(c *fiber.Ctx) error { db := boltdb.New() + config, err := db.GetConfig() + if err != nil { + return err + } + // set bucket - err := db.SetBucket("machine") + err = db.SetBucket("machine") if err != nil { return err } @@ -28,6 +33,9 @@ func List(c *fiber.Ctx) error { if err != nil { return err } + if machine.Interval == 0 { + machine.Interval = config.General.Interval + } if machine.Inactive { inactiveMachines = append(inactiveMachines, machine) } else { diff --git a/app/store/boltdb/boltdb.go b/app/store/boltdb/boltdb.go index 2d2b4b5..732e883 100644 --- a/app/store/boltdb/boltdb.go +++ b/app/store/boltdb/boltdb.go @@ -68,6 +68,21 @@ func (bolt *Bolt) Get(key string, result interface{}, bucket string) error { return err } +func (bolt *Bolt) GetConfig() (structs.Config, error) { + var data []byte + var result structs.Config + err := bolt.db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket([]byte("config")) + data = bucket.Get([]byte("main")) + err := json.Unmarshal(data, &result) + if err != nil { + return err + } + return nil + }) + return result, err +} + func (bolt *Bolt) GetLastByName(id string, bucket string) ([]byte, error) { var result []byte err := bolt.db.View(func(tx *bbolt.Tx) error { diff --git a/app/structs/machines.go b/app/structs/machines.go index 1633806..63d2b7a 100644 --- a/app/structs/machines.go +++ b/app/structs/machines.go @@ -30,4 +30,5 @@ type Machine struct { Days int `json:"days"` // remaining days Status string `json:"status"` // status color Inactive bool `json:"inactive"` + Interval int `json:"interval"` } diff --git a/app/views/machine/edit.html b/app/views/machine/edit.html index 9c3d336..b9958dc 100644 --- a/app/views/machine/edit.html +++ b/app/views/machine/edit.html @@ -7,6 +7,10 @@ +
+ + +
{{ end }} + LTS
From 02e03af436b47664588dcd86066e76fcb6548ffd Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Tue, 4 Mar 2025 10:46:23 +0100 Subject: [PATCH 04/17] extracted logic from dashboard/list into function GetActiveMachines --- app/handler/dashboard/filter/operator.go | 79 +++------------------ app/handler/dashboard/list.go | 88 +----------------------- app/store/boltdb/boltdb.go | 84 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 156 deletions(-) diff --git a/app/handler/dashboard/filter/operator.go b/app/handler/dashboard/filter/operator.go index 3eda48b..12e26f7 100644 --- a/app/handler/dashboard/filter/operator.go +++ b/app/handler/dashboard/filter/operator.go @@ -1,92 +1,29 @@ package filter import ( - "encoding/json" - "sort" - "time" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/typomedia/patchouli/app/helper" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" ) func Operator(c *fiber.Ctx) error { - id := c.Params("id") + operatorId := c.Params("id") db := boltdb.New() + defer db.Close() - err := db.SetBucket("machine") + machines, err := db.GetActiveMachines() if err != nil { return err } - machines, _ := db.GetAllByOperatorId(id, "machine") - - Machines := structs.Machines{} - for _, v := range machines { - machine := structs.Machine{} - err = json.Unmarshal(v, &machine) - if err != nil { - return err + var operatorMachines structs.Machines + for _, m := range machines { + if m.Operator.Id == operatorId { + operatorMachines = append(operatorMachines, m) } - - lastUpdate, _ := db.GetLastByName(machine.Id, "history") - - update := structs.Update{} - err = json.Unmarshal(lastUpdate, &update) - if err != nil { - log.Error(err) - } - - machine.Update = update - - Machines = append(Machines, machine) - - } - - // sort machines by oldest update first - sort.Sort(structs.ByDate(Machines)) - - err = db.SetBucket("config") - if err != nil { - return err } - var config structs.Config - db.Get("main", &config, "config") - - defer db.Close() - - interval := config.General.Interval - for i := range Machines { - currentDate := time.Now() - - if Machines[i].Update.Date == "" { - Machines[i].Update.Date = "0000-00-00" - Machines[i].Status = "danger" - continue - } - - Machines[i].Update.Date = helper.UnixToDateString(Machines[i].Update.Date) - - date, err := time.Parse("2006-01-02", Machines[i].Update.Date) - if err != nil { - log.Error(err) - } - - Machines[i].Days = int(currentDate.Sub(date).Hours() / 24) - if Machines[i].Days > interval { - Machines[i].Status = "danger" - } else if Machines[i].Days > interval/3 { - Machines[i].Status = "warning" - } else { - Machines[i].Status = "success" - } - Machines[i].Days = interval - int(currentDate.Sub(date).Hours()/24) - - } return c.Render("app/views/dashboard/list", fiber.Map{ - "Machines": Machines, + "Machines": operatorMachines, }) } diff --git a/app/handler/dashboard/list.go b/app/handler/dashboard/list.go index 1d253bc..4f5ed34 100644 --- a/app/handler/dashboard/list.go +++ b/app/handler/dashboard/list.go @@ -1,102 +1,20 @@ package dashboard import ( - "encoding/json" - "sort" - "time" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/typomedia/patchouli/app/helper" "github.com/typomedia/patchouli/app/store/boltdb" - "github.com/typomedia/patchouli/app/structs" ) func List(c *fiber.Ctx) error { db := boltdb.New() + defer db.Close() - err := db.SetBucket("machine") - if err != nil { - return err - } - - machines, _ := db.GetAll("machine") - - Machines := structs.Machines{} - for _, v := range machines { - machine := structs.Machine{} - err = json.Unmarshal(v, &machine) - if err != nil { - return err - } - - lastUpdate, _ := db.GetLastByName(machine.Id, "history") - - if lastUpdate != nil { - update := structs.Update{} - err = json.Unmarshal(lastUpdate, &update) - if err != nil { - log.Error(err) - } - machine.Update = update - } - - if !machine.Inactive { - Machines = append(Machines, machine) - } - - } - - // sort machines by oldest update first - sort.Sort(structs.ByDate(Machines)) - - err = db.SetBucket("config") + machines, err := db.GetActiveMachines() if err != nil { return err } - var config structs.Config - db.Get("main", &config, "config") - - defer db.Close() - - for i := range Machines { - currentDate := time.Now() - - if Machines[i].Update.Date == "" { - Machines[i].Update.Date = "0000-00-00" - Machines[i].Status = "danger" - continue - } - - Machines[i].Update.Date = helper.UnixToDateString(Machines[i].Update.Date) - - date, err := time.Parse(time.DateOnly, Machines[i].Update.Date) - if err != nil { - log.Error(err) - } - - var interval int - // check if machine has custom interval - if Machines[i].Interval != 0 { - interval = Machines[i].Interval - } else { - interval = config.General.Interval - } - - Machines[i].Days = int(currentDate.Sub(date).Hours() / 24) - if Machines[i].Days > interval { - Machines[i].Status = "danger" - } else if Machines[i].Days > interval/3 { - Machines[i].Status = "warning" - } else { - Machines[i].Status = "success" - } - Machines[i].Days = interval - int(currentDate.Sub(date).Hours()/24) - - } - return c.Render("app/views/dashboard/list", fiber.Map{ - "Machines": Machines, + "Machines": machines, }) } diff --git a/app/store/boltdb/boltdb.go b/app/store/boltdb/boltdb.go index 732e883..5532958 100644 --- a/app/store/boltdb/boltdb.go +++ b/app/store/boltdb/boltdb.go @@ -2,6 +2,8 @@ package boltdb import ( "encoding/json" + "github.com/typomedia/patchouli/app/helper" + "sort" "time" "github.com/typomedia/patchouli/app/structs" @@ -182,6 +184,88 @@ func (bolt *Bolt) GetAllByOperatorId(id string, bucket string) ([][]byte, error) return result, err } +func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { + machines, _ := bolt.GetAll("machine") + + Machines := structs.Machines{} + for _, v := range machines { + machine := structs.Machine{} + err := json.Unmarshal(v, &machine) + if err != nil { + return nil, err + } + + lastUpdate, _ := bolt.GetLastByName(machine.Id, "history") + + if lastUpdate != nil { + update := structs.Update{} + err = json.Unmarshal(lastUpdate, &update) + if err != nil { + return nil, err + } + machine.Update = update + } + + if !machine.Inactive { + Machines = append(Machines, machine) + } + } + // sort machines by oldest update first + sort.Sort(structs.ByDate(Machines)) + + config, err := bolt.GetConfig() + if err != nil { + return nil, err + } + + for i := range Machines { + currentDate := time.Now() + + if Machines[i].Update.Date == "" { + Machines[i].Update.Date = "0000-00-00" + Machines[i].Status = "danger" + continue + } + + Machines[i].Update.Date = helper.UnixToDateString(Machines[i].Update.Date) + + date, err := time.Parse(time.DateOnly, Machines[i].Update.Date) + if err != nil { + return nil, err + } + + var interval int + // check if machine has custom interval + if Machines[i].Interval != 0 { + interval = Machines[i].Interval + } else { + interval = config.General.Interval + } + + Machines[i].Days = int(currentDate.Sub(date).Hours() / 24) + if Machines[i].Days > interval { + Machines[i].Status = "danger" + } else if Machines[i].Days > interval/3 { + Machines[i].Status = "warning" + } else { + Machines[i].Status = "success" + } + Machines[i].Days = interval - int(currentDate.Sub(date).Hours()/24) + + } + + return Machines, nil +} + +func (bolt *Bolt) GetOperatorById(id string) (structs.Operator, error) { + var operator structs.Operator + err := bolt.Get(id, &operator, "operator") + if err != nil { + return operator, err + } + return operator, nil +} + func (bolt *Bolt) Close() error { return bolt.db.Close() } From 6d4dd35a1c2d0044a26d1406cd1e1f5c383dae0d Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Tue, 4 Mar 2025 10:46:44 +0100 Subject: [PATCH 05/17] use function GetConfig in mail.go --- app/handler/update/mail.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/handler/update/mail.go b/app/handler/update/mail.go index 83cbec9..a4e5838 100644 --- a/app/handler/update/mail.go +++ b/app/handler/update/mail.go @@ -36,8 +36,7 @@ func Mail(c *fiber.Ctx) error { log.Error(err) } - var config structs.Config - err = db.Get("main", &config, "config") + config, err := db.GetConfig() if err != nil { log.Error(err) } From ff302ef15da69ee474c13234aabed0ccc806a313 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Tue, 4 Mar 2025 10:46:59 +0100 Subject: [PATCH 06/17] upgraded packages --- go.mod | 36 +++++++++++++++------------- go.sum | 75 ++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 60 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index f7822b8..2be0bde 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,32 @@ module github.com/typomedia/patchouli -go 1.20 +go 1.23.0 + +toolchain go1.23.2 require ( + github.com/Showmax/go-fqdn v1.0.0 github.com/XotoX1337/tinymail v0.7.0 - github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a - github.com/gofiber/fiber/v2 v2.52.0 - github.com/gofiber/template/html/v2 v2.0.5 - github.com/gorilla/schema v1.2.1 + github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 + github.com/gofiber/fiber/v2 v2.52.6 + github.com/gofiber/template/html/v2 v2.1.3 + github.com/gorilla/schema v1.4.1 github.com/oklog/ulid/v2 v2.1.0 - go.etcd.io/bbolt v1.3.8 - golang.org/x/crypto v0.32.0 + go.etcd.io/bbolt v1.4.0 + golang.org/x/crypto v0.35.0 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/gofiber/template v1.8.2 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/utils v1.1.0 // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.29.0 // indirect + github.com/valyala/fasthttp v1.59.0 // indirect + golang.org/x/sys v0.30.0 // indirect ) diff --git a/go.sum b/go.sum index f976efc..046d25b 100644 --- a/go.sum +++ b/go.sum @@ -1,50 +1,57 @@ +github.com/Showmax/go-fqdn v1.0.0 h1:0rG5IbmVliNT5O19Mfuvna9LL7zlHyRfsSvBPZmF9tM= +github.com/Showmax/go-fqdn v1.0.0/go.mod h1:SfrFBzmDCtCGrnHhoDjuvFnKsWjEQX/Q9ARZvOrJAko= github.com/XotoX1337/tinymail v0.7.0 h1:KvIaWvUSorZTh+DiY/iJfH5P7hjLen2AaWnelIJfY24= github.com/XotoX1337/tinymail v0.7.0/go.mod h1:eQ8xKUuxppTrCGPkBAtROv20OK1eRw9rsVto9yuwgPY= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA= -github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= -github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= -github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk= -github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= -github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20= -github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ= +github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= +github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= +github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= +github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o= +github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE= github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM= -github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= -github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI= +github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From a3db5bf8a9f67b02d574034ad60e783c4f9b7320 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Tue, 4 Mar 2025 10:49:58 +0100 Subject: [PATCH 07/17] added notifier notifies operators once a day who have machines that need to be updated for at least 7 days --- app/app.go | 11 +- app/notifier/notifier.go | 83 +++++++ main.go | 29 +++ public/html/mail/notify.html | 448 +++++++++++++++++++++++++++++++++++ 4 files changed, 566 insertions(+), 5 deletions(-) create mode 100644 app/notifier/notifier.go create mode 100644 public/html/mail/notify.html diff --git a/app/app.go b/app/app.go index cd2e222..866e93c 100644 --- a/app/app.go +++ b/app/app.go @@ -3,11 +3,12 @@ package app var app *Application type Application struct { - Name string - Version string - Author string - Description string - MailTemplate string + Name string + Version string + Author string + Description string + MailTemplate string + NotifyTemplate string } func new() *Application { diff --git a/app/notifier/notifier.go b/app/notifier/notifier.go new file mode 100644 index 0000000..d7df9b5 --- /dev/null +++ b/app/notifier/notifier.go @@ -0,0 +1,83 @@ +package notifier + +import ( + "github.com/XotoX1337/tinymail" + "github.com/gofiber/fiber/v2/log" + patchouli "github.com/typomedia/patchouli/app" + "github.com/typomedia/patchouli/app/store/boltdb" + "github.com/typomedia/patchouli/app/structs" +) + +const NOTIFY_DAYS = 7 + +type Notifier struct { + Hostname string + Operators map[string]structs.Machines +} + +func (n Notifier) Run() { + db := boltdb.New() + defer db.Close() + + config, err := db.GetConfig() + if err != nil { + log.Fatal(err) + } + + machines, err := db.GetActiveMachines() + if err != nil { + log.Fatal(err) + } + n.Operators = make(map[string]structs.Machines) + for _, machine := range machines { + if machine.Days > NOTIFY_DAYS { + continue + } + n.Operators[machine.Operator.Id] = append(n.Operators[machine.Operator.Id], machine) + } + + opts := tinymail.MailerOpts{ + User: config.Smtp.Username, + Password: config.Smtp.Password, + Host: config.Smtp.Host, + Port: config.Smtp.Port, + } + + mailer, err := tinymail.New(opts) + if err != nil { + log.Fatal(err) + } + + for operatorId, machines := range n.Operators { + + operator, err := db.GetOperatorById(operatorId) + if err != nil { + log.Fatal(err) + } + + tplData := map[string]interface{}{ + "Machines": machines, + "Operator": operator, + "Host": n.Hostname, + "Version": patchouli.GetApp().Version, + "App": patchouli.GetApp().Name, + } + + msg, err := tinymail.FromTemplateString(tplData, patchouli.GetApp().NotifyTemplate) + if err != nil { + log.Error(err) + } + + msg.SetFrom(config.Smtp.Sender) + msg.SetTo(config.General.Email) + msg.SetSubject("Patchmgmt: Maschinen benötigen Updates!") + + mailer.SetMessage(msg) + + err = mailer.SetMessage(msg).Send() + if err != nil { + log.Fatal(err) + } + break + } +} diff --git a/main.go b/main.go index 258a9f1..254cc65 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "embed" "fmt" + "github.com/Showmax/go-fqdn" + "github.com/typomedia/patchouli/app/notifier" "log" "net/http" "time" @@ -33,9 +35,13 @@ var public embed.FS //go:embed public/html/mail/update.html var mailTemplate string +//go:embed public/html/mail/notify.html +var notifyTemplate string + func main() { App := patchouli.GetApp() App.MailTemplate = mailTemplate + App.NotifyTemplate = notifyTemplate engine := html.NewFileSystem(http.FS(views), ".html") engine.AddFunc("Name", func() string { return App.Name @@ -95,5 +101,28 @@ func main() { PathPrefix: "public", })) + app.Hooks().OnListen(func(listenData fiber.ListenData) error { + if fiber.IsChild() { + return nil + } + scheme := "http" + if listenData.TLS { + scheme = "https" + } + + hostname, err := fqdn.FqdnHostname() + if err != nil { + return err + } + + n := notifier.Notifier{ + Hostname: scheme + "://" + hostname, + } + g := gron.New() + g.Add(gron.Every(24*time.Hour), n) + g.Start() + return nil + }) + log.Fatal(app.Listen(":5000")) } diff --git a/public/html/mail/notify.html b/public/html/mail/notify.html new file mode 100644 index 0000000..f0da92d --- /dev/null +++ b/public/html/mail/notify.html @@ -0,0 +1,448 @@ + + + + + + Patchouli - Stay secure + + + + Der Server wurde aktualisiert + + + + + + + + + \ No newline at end of file From 60595e35cb4355221a175b222b2536b86b49d17b Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 09:09:47 +0200 Subject: [PATCH 08/17] added encryption * app config is now stored in App struct * first time cipher key is retrieved, a new one will be generated and stored in bolt * SMTP password will now be encrypted --- .gitignore | 3 +- app/app.go | 54 ++++++++++++++++++++++++++++++ app/encryption/encryption.go | 64 ++++++++++++++++++++++++++++++++++++ app/handler/config/save.go | 15 +++++++++ app/handler/update/mail.go | 8 ++++- app/structs/config.go | 38 +++++++++++++++++++-- go.mod | 20 +++++------ go.sum | 35 ++++++++++---------- main.go | 27 +++++---------- public/img/favicon.svg | 5 ++- public/img/patchouli.svg | 4 ++- 11 files changed, 219 insertions(+), 54 deletions(-) create mode 100644 app/encryption/encryption.go diff --git a/.gitignore b/.gitignore index 1ed37ab..7009ece 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .vscode/ dist/ *.boltdb -tmp/ \ No newline at end of file +tmp/ +/vendor/ diff --git a/app/app.go b/app/app.go index 866e93c..f178672 100644 --- a/app/app.go +++ b/app/app.go @@ -1,5 +1,10 @@ package app +import ( + "github.com/typomedia/patchouli/app/store/boltdb" + "github.com/typomedia/patchouli/app/structs" +) + var app *Application type Application struct { @@ -9,6 +14,7 @@ type Application struct { Description string MailTemplate string NotifyTemplate string + Config structs.Config } func new() *Application { @@ -17,9 +23,57 @@ func new() *Application { app.Version = "0.2.1" app.Author = "Philipp Speck " app.Description = "Patch Management Planner" + err := app.LoadConfig() + if err != nil { + panic(err) + } + if app.Config.Security.Generated { + err = app.StoreConfig() + if err != nil { + panic(err) + } + } return app } + +func (app *Application) StoreConfig() error { + db := boltdb.New() + defer db.Close() + + err := db.SetBucket("config") + if err != nil { + return err + } + + err = db.Set("main", app.Config, "config") + if err != nil { + return err + } + + return nil +} + +func (app *Application) LoadConfig() error { + db := boltdb.New() + defer db.Close() + + config, err := db.GetConfig() + if err != nil { + return err + } + + if !config.Security.Generated { + err = config.GenerateCipherKey() + if err != nil { + return err + } + } + + app.Config = config + return nil +} + func GetApp() *Application { if app == nil { app = new() diff --git a/app/encryption/encryption.go b/app/encryption/encryption.go new file mode 100644 index 0000000..69ff2b8 --- /dev/null +++ b/app/encryption/encryption.go @@ -0,0 +1,64 @@ +package encryption + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "github.com/typomedia/patchouli/app" + "io" +) + +func EncryptString(stringToEncrypt string) (string, error) { + key, err := app.GetApp().Config.GetCipherKey() + if err != nil { + return "", err + } + aesKey, _ := hex.DecodeString(key) + plaintext := []byte(stringToEncrypt) + block, err := aes.NewCipher(aesKey) + if err != nil { + return "", err + } + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, aesGCM.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil) + return fmt.Sprintf("%x", ciphertext), nil +} + +func DecryptString(encString string) (string, error) { + key, err := app.GetApp().Config.GetCipherKey() + + aesKey, _ := hex.DecodeString(key) + enc, _ := hex.DecodeString(encString) + block, err := aes.NewCipher(aesKey) + if err != nil { + return "", err + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + nonceSize := aesGCM.NonceSize() + if len(enc) < nonceSize { + return "", errors.New("ciphertext too short") + } + nonce, ciphertext := enc[:nonceSize], enc[nonceSize:] + plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", err + } + + return string(plaintext), nil +} diff --git a/app/handler/config/save.go b/app/handler/config/save.go index 0994a77..a66f32a 100644 --- a/app/handler/config/save.go +++ b/app/handler/config/save.go @@ -3,6 +3,8 @@ package config import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" + "github.com/typomedia/patchouli/app" + "github.com/typomedia/patchouli/app/encryption" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" ) @@ -20,8 +22,21 @@ func Save(c *fiber.Ctx) error { log.Error(err) } + // update config from body with security params from app + config.Security = app.GetApp().Config.Security + + _, err = encryption.DecryptString(config.Smtp.Password) + if err != nil { // config.Smtp.Password is not encrypted, encrypt it + config.Smtp.Password, err = encryption.EncryptString(config.Smtp.Password) + if err != nil { + log.Error(err) + } + } + db.Set("main", config, "config") + app.GetApp().Config = config + defer db.Close() return c.Redirect("/config") diff --git a/app/handler/update/mail.go b/app/handler/update/mail.go index a4e5838..7c816b0 100644 --- a/app/handler/update/mail.go +++ b/app/handler/update/mail.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" patchouli "github.com/typomedia/patchouli/app" + "github.com/typomedia/patchouli/app/encryption" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" ) @@ -41,9 +42,14 @@ func Mail(c *fiber.Ctx) error { log.Error(err) } + smtpPasswd, err := encryption.DecryptString(config.Smtp.Password) + if err != nil { + log.Error(err) + } + opts := tinymail.MailerOpts{ User: config.Smtp.Username, - Password: config.Smtp.Password, + Password: smtpPasswd, Host: config.Smtp.Host, Port: config.Smtp.Port, } diff --git a/app/structs/config.go b/app/structs/config.go index b8b781a..6e692a7 100644 --- a/app/structs/config.go +++ b/app/structs/config.go @@ -1,14 +1,21 @@ package structs +import ( + "crypto/rand" + "encoding/hex" +) + type Config struct { - General General `json:"general"` - Smtp Smtp `json:"smtp"` + General General `json:"general"` + Smtp Smtp `json:"smtp"` + Security Security `json:"security"` } type General struct { Company string `json:"company"` Email string `json:"email"` Interval int `json:"interval"` + Hostname string `json:"hostname"` } type Smtp struct { @@ -18,3 +25,30 @@ type Smtp struct { Username string `json:"username"` Password string `json:"password"` } + +type Security struct { + CipherKey string `json:"cipher_key"` + Generated bool `json:"generated"` +} + +func (c *Config) GenerateCipherKey() error { + bytes := make([]byte, 32) + if _, err := rand.Read(bytes); err != nil { + return err + } + c.Security.CipherKey = hex.EncodeToString(bytes) + c.Security.Generated = true + + return nil +} + +func (c *Config) GetCipherKey() (string, error) { + if c.Security.CipherKey != "" { + return c.Security.CipherKey, nil + } + err := c.GenerateCipherKey() + if err != nil { + return "", err + } + return c.Security.CipherKey, nil +} diff --git a/go.mod b/go.mod index 2be0bde..0f54c6b 100644 --- a/go.mod +++ b/go.mod @@ -1,32 +1,30 @@ module github.com/typomedia/patchouli -go 1.23.0 - -toolchain go1.23.2 +go 1.24.1 require ( - github.com/Showmax/go-fqdn v1.0.0 github.com/XotoX1337/tinymail v0.7.0 github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 github.com/gofiber/fiber/v2 v2.52.6 github.com/gofiber/template/html/v2 v2.1.3 github.com/gorilla/schema v1.4.1 github.com/oklog/ulid/v2 v2.1.0 + github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46 go.etcd.io/bbolt v1.4.0 - golang.org/x/crypto v0.35.0 ) require ( - github.com/andybalholm/brotli v1.1.1 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.59.0 // indirect - golang.org/x/sys v0.30.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.29.0 // indirect ) diff --git a/go.sum b/go.sum index 046d25b..2cfe0c4 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/Showmax/go-fqdn v1.0.0 h1:0rG5IbmVliNT5O19Mfuvna9LL7zlHyRfsSvBPZmF9tM= -github.com/Showmax/go-fqdn v1.0.0/go.mod h1:SfrFBzmDCtCGrnHhoDjuvFnKsWjEQX/Q9ARZvOrJAko= github.com/XotoX1337/tinymail v0.7.0 h1:KvIaWvUSorZTh+DiY/iJfH5P7hjLen2AaWnelIJfY24= github.com/XotoX1337/tinymail v0.7.0/go.mod h1:eQ8xKUuxppTrCGPkBAtROv20OK1eRw9rsVto9yuwgPY= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ= @@ -20,10 +18,11 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= @@ -33,25 +32,25 @@ github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46 h1:dp1iW1JOTY63249ZTwxzwN0EKG6EvuPdfMohKo4EomY= +github.com/roylee0704/gron v0.0.0-20160621042432-e78485adab46/go.mod h1:MDhl6ujYU3dbEiGclVk5uA4pHEjTS659POKAtiAWB94= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI= -github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 254cc65..7822184 100644 --- a/main.go +++ b/main.go @@ -3,15 +3,12 @@ package main import ( "embed" "fmt" - "github.com/Showmax/go-fqdn" - "github.com/typomedia/patchouli/app/notifier" - "log" - "net/http" - "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/template/html/v2" + "github.com/roylee0704/gron" + "github.com/roylee0704/gron/xtime" patchouli "github.com/typomedia/patchouli/app" "github.com/typomedia/patchouli/app/handler/api/csv" "github.com/typomedia/patchouli/app/handler/api/htmx" @@ -24,6 +21,9 @@ import ( "github.com/typomedia/patchouli/app/handler/operator" "github.com/typomedia/patchouli/app/handler/system" "github.com/typomedia/patchouli/app/handler/update" + "github.com/typomedia/patchouli/app/notifier" + "net/http" + "time" ) //go:embed app/views @@ -105,21 +105,10 @@ func main() { if fiber.IsChild() { return nil } - scheme := "http" - if listenData.TLS { - scheme = "https" - } - hostname, err := fqdn.FqdnHostname() - if err != nil { - return err - } - - n := notifier.Notifier{ - Hostname: scheme + "://" + hostname, - } + n := notifier.Notifier{} g := gron.New() - g.Add(gron.Every(24*time.Hour), n) + g.Add(gron.Every(1*xtime.Week), n) g.Start() return nil }) diff --git a/public/img/favicon.svg b/public/img/favicon.svg index b51809d..5d62ac4 100644 --- a/public/img/favicon.svg +++ b/public/img/favicon.svg @@ -1 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/patchouli.svg b/public/img/patchouli.svg index b4bcfc6..0609200 100644 --- a/public/img/patchouli.svg +++ b/public/img/patchouli.svg @@ -1 +1,3 @@ - \ No newline at end of file + \ No newline at end of file From 0c0d22cf29d0c4696ba03c6e94a78dbdfc5e5f46 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 09:10:08 +0200 Subject: [PATCH 09/17] added field hostname to config --- app/handler/config/edit.go | 25 +++++-------------------- app/views/config/edit.html | 4 ++++ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/app/handler/config/edit.go b/app/handler/config/edit.go index e96afb6..39c035a 100644 --- a/app/handler/config/edit.go +++ b/app/handler/config/edit.go @@ -1,31 +1,16 @@ package config import ( + "fmt" "github.com/gofiber/fiber/v2" - "github.com/typomedia/patchouli/app/store/boltdb" - "golang.org/x/crypto/bcrypt" + "github.com/typomedia/patchouli/app" ) func Edit(c *fiber.Ctx) error { - db := boltdb.New() - - err := db.SetBucket("config") - if err != nil { - return err - } - - config, err := db.GetConfig() - if err != nil { - return err - } - if config.Smtp.Password != "" { - passwordHash, err := bcrypt.GenerateFromPassword([]byte(config.Smtp.Password), bcrypt.DefaultCost) - if err != nil { - return err - } - config.Smtp.Password = string(passwordHash) + config := app.GetApp().Config + if config.General.Hostname == "" { + config.General.Hostname = fmt.Sprintf("%s://%s", c.Protocol(), c.Hostname()) } - defer db.Close() return c.Render("app/views/config/edit", fiber.Map{ "Config": config, }) diff --git a/app/views/config/edit.html b/app/views/config/edit.html index b1f5981..ce2e3c0 100644 --- a/app/views/config/edit.html +++ b/app/views/config/edit.html @@ -17,6 +17,10 @@

General

+
+ + +

Mailserver

From 84584d4fb460ce2111b84b12c940f62811263acf Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 09:11:01 +0200 Subject: [PATCH 10/17] improved notify email template added Firstname and Lastname functions to operator struct --- app/notifier/notifier.go | 25 +++++++++++++------------ app/structs/operators.go | 10 ++++++++++ public/html/mail/notify.html | 13 ++++++++----- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/app/notifier/notifier.go b/app/notifier/notifier.go index d7df9b5..74c9a60 100644 --- a/app/notifier/notifier.go +++ b/app/notifier/notifier.go @@ -3,7 +3,9 @@ package notifier import ( "github.com/XotoX1337/tinymail" "github.com/gofiber/fiber/v2/log" - patchouli "github.com/typomedia/patchouli/app" + "github.com/typomedia/patchouli/app" + + "github.com/typomedia/patchouli/app/encryption" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" ) @@ -11,7 +13,6 @@ import ( const NOTIFY_DAYS = 7 type Notifier struct { - Hostname string Operators map[string]structs.Machines } @@ -19,10 +20,7 @@ func (n Notifier) Run() { db := boltdb.New() defer db.Close() - config, err := db.GetConfig() - if err != nil { - log.Fatal(err) - } + config := app.GetApp().Config machines, err := db.GetActiveMachines() if err != nil { @@ -36,9 +34,13 @@ func (n Notifier) Run() { n.Operators[machine.Operator.Id] = append(n.Operators[machine.Operator.Id], machine) } + smtpPasswd, err := encryption.DecryptString(config.Smtp.Password) + if err != nil { + return + } opts := tinymail.MailerOpts{ User: config.Smtp.Username, - Password: config.Smtp.Password, + Password: smtpPasswd, Host: config.Smtp.Host, Port: config.Smtp.Port, } @@ -58,12 +60,12 @@ func (n Notifier) Run() { tplData := map[string]interface{}{ "Machines": machines, "Operator": operator, - "Host": n.Hostname, - "Version": patchouli.GetApp().Version, - "App": patchouli.GetApp().Name, + "Hostname": config.General.Hostname, + "Version": app.GetApp().Version, + "App": app.GetApp().Name, } - msg, err := tinymail.FromTemplateString(tplData, patchouli.GetApp().NotifyTemplate) + msg, err := tinymail.FromTemplateString(tplData, app.GetApp().NotifyTemplate) if err != nil { log.Error(err) } @@ -78,6 +80,5 @@ func (n Notifier) Run() { if err != nil { log.Fatal(err) } - break } } diff --git a/app/structs/operators.go b/app/structs/operators.go index ceb9a93..c608a52 100644 --- a/app/structs/operators.go +++ b/app/structs/operators.go @@ -1,5 +1,7 @@ package structs +import "strings" + type Operators []Operator type Operator struct { @@ -8,3 +10,11 @@ type Operator struct { Department string `json:"department"` Email string `json:"email"` } + +func (o Operator) Firstname() string { + return strings.Split(o.Name, " ")[0] +} + +func (o Operator) Lastname() string { + return strings.Split(o.Name, " ")[1] +} diff --git a/public/html/mail/notify.html b/public/html/mail/notify.html index f0da92d..d22c7ce 100644 --- a/public/html/mail/notify.html +++ b/public/html/mail/notify.html @@ -165,7 +165,7 @@ border-radius: 5px; text-align: center; } - .btn a { + .btn a { background-color: #ffffff; border: solid 1px #3498db; border-radius: 5px; @@ -337,7 +337,7 @@ - Der Server wurde aktualisiert + {{.Operator.Firstname}}, deine Server brauchen Dich! @@ -398,7 +398,7 @@

 

From 467b8eff4c17238b9d92b16f73c91004bc3f7af6 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 09:12:06 +0200 Subject: [PATCH 11/17] retrieve config from app instead of boltdb --- app/handler/machine/edit.go | 7 ++----- app/handler/machine/list.go | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/handler/machine/edit.go b/app/handler/machine/edit.go index e3139cf..b812cab 100644 --- a/app/handler/machine/edit.go +++ b/app/handler/machine/edit.go @@ -3,6 +3,7 @@ package machine import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/log" + "github.com/typomedia/patchouli/app" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" ) @@ -23,11 +24,7 @@ func Edit(c *fiber.Ctx) error { log.Error(err) } - var config structs.Config - config, err = db.GetConfig() - if err != nil { - return err - } + config := app.GetApp().Config if machine.Interval == 0 { machine.Interval = config.General.Interval } diff --git a/app/handler/machine/list.go b/app/handler/machine/list.go index 4d1c978..a9639f4 100644 --- a/app/handler/machine/list.go +++ b/app/handler/machine/list.go @@ -2,6 +2,7 @@ package machine import ( "encoding/json" + "github.com/typomedia/patchouli/app" "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" @@ -11,13 +12,10 @@ import ( func List(c *fiber.Ctx) error { db := boltdb.New() - config, err := db.GetConfig() - if err != nil { - return err - } + config := app.GetApp().Config // set bucket - err = db.SetBucket("machine") + err := db.SetBucket("machine") if err != nil { return err } From e2d399425ab286081104b881ee6c50d9653c468c Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 09:33:25 +0200 Subject: [PATCH 12/17] added hidden input field inactive to store active state of machines --- app/views/machine/edit.html | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/machine/edit.html b/app/views/machine/edit.html index b9958dc..8c82e78 100644 --- a/app/views/machine/edit.html +++ b/app/views/machine/edit.html @@ -2,6 +2,7 @@
+
From bdf3e9fd045aa3d2d7febdbe530a5a1b4756597e Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 11:25:24 +0200 Subject: [PATCH 13/17] added deactivation of operators --- app/handler/operator/activate.go | 35 ++++++++++++++++++++++++++++++ app/handler/operator/deactivate.go | 34 +++++++++++++++++++++++++++++ app/handler/operator/list.go | 19 ++-------------- app/store/boltdb/boltdb.go | 21 ++++++++++++++++++ app/structs/operators.go | 1 + app/views/machine/edit.html | 15 +++++-------- app/views/operator/edit.html | 15 ++++++++++++- app/views/operator/list.html | 8 +++++++ main.go | 3 +++ 9 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 app/handler/operator/activate.go create mode 100644 app/handler/operator/deactivate.go diff --git a/app/handler/operator/activate.go b/app/handler/operator/activate.go new file mode 100644 index 0000000..7b8852f --- /dev/null +++ b/app/handler/operator/activate.go @@ -0,0 +1,35 @@ +package operator + +import ( + "github.com/gofiber/fiber/v2" + "github.com/typomedia/patchouli/app/store/boltdb" + "github.com/typomedia/patchouli/app/structs" +) + +func Activate(c *fiber.Ctx) error { + operatorId := c.Params("id") + + db := boltdb.New() + + err := db.SetBucket("operator") + if err != nil { + return err + } + defer db.Close() + + var operator structs.Operator + err = db.Get(operatorId, &operator, "operator") + if err != nil { + return err + } + + operator.Inactive = false + + err = db.Set(operatorId, operator, "operator") + + if err != nil { + return err + } + + return c.Redirect("/operator") +} diff --git a/app/handler/operator/deactivate.go b/app/handler/operator/deactivate.go new file mode 100644 index 0000000..32e2590 --- /dev/null +++ b/app/handler/operator/deactivate.go @@ -0,0 +1,34 @@ +package operator + +import ( + "github.com/gofiber/fiber/v2" + "github.com/typomedia/patchouli/app/store/boltdb" + "github.com/typomedia/patchouli/app/structs" +) + +func Deactivate(c *fiber.Ctx) error { + operatorId := c.Params("id") + + db := boltdb.New() + + err := db.SetBucket("operator") + if err != nil { + return err + } + defer db.Close() + + var operator structs.Operator + err = db.Get(operatorId, &operator, "operator") + if err != nil { + return err + } + + operator.Inactive = true + err = db.Set(operatorId, operator, "operator") + + if err != nil { + return err + } + + return c.Redirect("/operator") +} diff --git a/app/handler/operator/list.go b/app/handler/operator/list.go index 7cfe4e1..e1c4cb5 100644 --- a/app/handler/operator/list.go +++ b/app/handler/operator/list.go @@ -1,36 +1,21 @@ package operator import ( - "encoding/json" "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" - "github.com/typomedia/patchouli/app/structs" ) func List(c *fiber.Ctx) error { db := boltdb.New() - err := db.SetBucket("operator") + operators, err := db.GetAllOperators() if err != nil { return err } - operators, _ := db.GetAll("operator") - - Operators := structs.Operators{} - for _, v := range operators { - operator := structs.Operator{} - err = json.Unmarshal(v, &operator) - if err != nil { - return err - } - Operators = append(Operators, operator) - - } - defer db.Close() return c.Render("app/views/operator/list", fiber.Map{ - "Operators": Operators, + "Operators": operators, }) } diff --git a/app/store/boltdb/boltdb.go b/app/store/boltdb/boltdb.go index 5532958..af3e86f 100644 --- a/app/store/boltdb/boltdb.go +++ b/app/store/boltdb/boltdb.go @@ -257,6 +257,27 @@ func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { return Machines, nil } +func (bolt *Bolt) GetAllOperators() (structs.Operators, error) { + operators, _ := bolt.GetAll("operator") + + Operators := structs.Operators{} + InactiveOperators := structs.Operators{} + for _, v := range operators { + operator := structs.Operator{} + err := json.Unmarshal(v, &operator) + if err != nil { + return nil, err + } + + if operator.Inactive { + InactiveOperators = append(InactiveOperators, operator) + } else { + Operators = append(Operators, operator) + } + } + return append(Operators, InactiveOperators...), nil +} + func (bolt *Bolt) GetOperatorById(id string) (structs.Operator, error) { var operator structs.Operator err := bolt.Get(id, &operator, "operator") diff --git a/app/structs/operators.go b/app/structs/operators.go index c608a52..35f5190 100644 --- a/app/structs/operators.go +++ b/app/structs/operators.go @@ -9,6 +9,7 @@ type Operator struct { Name string `json:"operator"` Department string `json:"department"` Email string `json:"email"` + Inactive bool `json:"inactive"` } func (o Operator) Firstname() string { diff --git a/app/views/machine/edit.html b/app/views/machine/edit.html index 8c82e78..fd4f95d 100644 --- a/app/views/machine/edit.html +++ b/app/views/machine/edit.html @@ -54,19 +54,16 @@
- {{ if .Machine.Inactive}} -
- -
- {{ else }} -
- -
- {{ end }}
{{ if .Machine.Inactive }} + {{ else }} + {{ end }} diff --git a/app/views/operator/edit.html b/app/views/operator/edit.html index c8fad85..8aed312 100644 --- a/app/views/operator/edit.html +++ b/app/views/operator/edit.html @@ -2,6 +2,7 @@
+
@@ -14,9 +15,21 @@
+
-
+
+ {{ if .Operator.Inactive }} + + + {{ else }} + + + {{ end }}
diff --git a/app/views/operator/list.html b/app/views/operator/list.html index 09d3fc9..b22c8dd 100644 --- a/app/views/operator/list.html +++ b/app/views/operator/list.html @@ -5,6 +5,7 @@ New Department Email + Status @@ -13,6 +14,13 @@ {{.Name}} {{.Department}} {{ .Email }} + + {{ if .Inactive }} + inactive + {{ else }} + active + {{ end }} + {{end}} diff --git a/main.go b/main.go index 7822184..c84f569 100644 --- a/main.go +++ b/main.go @@ -81,6 +81,9 @@ func main() { app.Get("/operator/edit/:id", operator.Edit) app.Post("/operator/save/:id", operator.Save) + app.Get("/operator/deactivate/:id", operator.Deactivate) + app.Get("/operator/activate/:id", operator.Activate) + app.Get("/system", system.List) app.Get("/system/new", system.New) app.Get("/system/edit/:id", system.Edit) From 1dea8fc8ec636d43be81b3cc504dd61524e9489b Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 16:41:54 +0200 Subject: [PATCH 14/17] added EOL icon and tooltip in machine/list --- app/structs/systems.go | 19 +++++++++++++++---- app/views/machine/list.html | 10 +++++++++- public/css/styles.css | 7 +++++-- public/css/styles.css.map | 2 +- public/css/styles.less | 13 ++++++++++--- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/app/structs/systems.go b/app/structs/systems.go index db9c537..8d7332b 100644 --- a/app/structs/systems.go +++ b/app/structs/systems.go @@ -1,10 +1,21 @@ package structs +import "time" + type Systems []System type System struct { - Id string - Name string - LTS bool - EOL string + Id string + Name string + LTS bool + EOL string + MachineCount int +} + +func (s System) IsEOL() bool { + eolTime, err := time.Parse(time.DateOnly, s.EOL) + if err != nil { + return false + } + return eolTime.UTC().Before(time.Now().UTC()) } diff --git a/app/views/machine/list.html b/app/views/machine/list.html index b7a8101..c6f7ddb 100644 --- a/app/views/machine/list.html +++ b/app/views/machine/list.html @@ -18,7 +18,15 @@ {{ .Name }} {{ .Interval }} - {{ .System.Name }} + + {{ if .System.IsEOL }} + + + + {{ end }} + {{ .System.Name }} + {{ .Ip }} {{ .Fqdn }} {{if .Operator.Email}} diff --git a/public/css/styles.css b/public/css/styles.css index ea6fb7e..91f9d66 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -72,10 +72,13 @@ a:hover { background: #ffcccc; } .success { - background: #ccfecc; + background: #f8f9c7; } .warning { - background: #f8f9c7; + background: #ccfecc; +} +i.ri-error-warning-line { + color: #D93526; } i.mail-success { color: #23af23; diff --git a/public/css/styles.css.map b/public/css/styles.css.map index 1de1c61..93ce96c 100644 --- a/public/css/styles.css.map +++ b/public/css/styles.css.map @@ -1 +1 @@ -{"version":3,"sources":["styles.less"],"names":[],"mappings":";;;;AAQA;EACE,aAJY,qBAIZ;EACA,iBAAA;;AAFF,IAGE;EACE,sBAAA;EACA,mBAAA;EACA,kCAAA;;AANJ,IAGE,IAIE;EACE,cAAA;;AARN,IAGE,IAOE;EACE,aAAa,8BAAb;EACA,iBAAA;EACA,yBAAA;EACA,eAAA;;AAdN,IAiBE;EACE,aAAA;EACA,sBAAA;EACA,UAAA;;AApBJ,IAiBE,KAIE;EACE,eAAA;;AAKI,IAVR,KAOE,MAAK,KACH,MACE,GACG;EACC,SAAS,OAAT;EACA,aAAa,WAAb;EACA,gBAAA;EACA,mBAAA;EACA,WAAA;;AAhCZ,IAiBE,KAOE,MAAK,KAYH,MACE,GAAE,UAAU;EACV,mBAAA;;AAtCV,IAiBE,KAOE,MAAK,KAYH,MAIE,GAAE;EACA,mBAAA;;AAzCV,IA8CE;EACE,UAAA;EACA,kBAAA;;AAhDJ,IA8CE,OAGE;EACE,gBAAA;;AAlDN,IA8CE,OAME;EACE,qBAAA;EACA,WAAA;EACA,qBAAA;;AAKN;AAAI;AAAI;AAAI;AAAI;AAAI;EAClB,aAhEY,qBAgEZ;;AAGF;EACE,iBAAA;EACA,eAAA;;AAGF,CAAC;EACC,qBAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF,CAAC;EACC,cAAA;EACA,eAAA;;AAEF,CAAC;EACC,cAAA;EACA,eAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF,cAAc,IAAI;EAChB,eAAe,0BAA0B,yBAAzC;EACA,qBAAA;EACA,YAAA","file":"styles.css"} \ No newline at end of file +{"version":3,"sources":["styles.less"],"names":[],"mappings":";;;;AAWA;EACE,aAPY,qBAOZ;EACA,iBAAA;;AAFF,IAGE;EACE,sBAAA;EACA,mBAAA;EACA,kCAAA;;AANJ,IAGE,IAIE;EACE,cAAA;;AARN,IAGE,IAOE;EACE,aAAa,8BAAb;EACA,iBAAA;EACA,yBAAA;EACA,eAAA;;AAdN,IAiBE;EACE,aAAA;EACA,sBAAA;EACA,UAAA;;AApBJ,IAiBE,KAIE;EACE,eAAA;;AAKI,IAVR,KAOE,MAAK,KACH,MACE,GACG;EACC,SAAS,OAAT;EACA,aAAa,WAAb;EACA,gBAAA;EACA,mBAAA;EACA,WAAA;;AAhCZ,IAiBE,KAOE,MAAK,KAYH,MACE,GAAE,UAAU;EACV,mBAAA;;AAtCV,IAiBE,KAOE,MAAK,KAYH,MAIE,GAAE;EACA,mBAAA;;AAzCV,IA8CE;EACE,UAAA;EACA,kBAAA;;AAhDJ,IA8CE,OAGE;EACE,gBAAA;;AAlDN,IA8CE,OAME;EACE,qBAAA;EACA,WAAA;EACA,qBAAA;;AAKN;AAAI;AAAI;AAAI;AAAI;AAAI;EAClB,aAnEY,qBAmEZ;;AAGF;EACE,iBAAA;EACA,eAAA;;AAGF,CAAC;EACC,qBAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF,CAAC;EACG,cAAA;;AAGJ,CAAC;EACC,cAAA;EACA,eAAA;;AAEF,CAAC;EACC,cAAA;EACA,eAAA;;AAGF;EACE,mBAAA;;AAGF;EACE,mBAAA;;AAGF,cAAc,IAAI;EAChB,eAAe,0BAA0B,yBAAzC;EACA,qBAAA;EACA,YAAA","file":"styles.css"} \ No newline at end of file diff --git a/public/css/styles.less b/public/css/styles.less index 358e39f..415dec9 100644 --- a/public/css/styles.less +++ b/public/css/styles.less @@ -5,6 +5,9 @@ @font-family: 'Signika', sans-serif; @color-primary: rgb(65, 84, 98); +@color-danger: #ffcccc; +@color-warning: #ccfecc; +@color-success: #f8f9c7; body { font-family: @font-family; @@ -80,15 +83,19 @@ a:hover { } .danger { - background: #ffcccc; + background: @color-danger; } .success { - background: #ccfecc; + background: @color-success; } .warning { - background: #f8f9c7; + background: @color-warning; +} + +i.ri-error-warning-line { + color: #D93526; } i.mail-success { From 36e9f15c4df778e14c7378a826c4b6535715224c Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 16:42:51 +0200 Subject: [PATCH 15/17] added MachineCount to system/list --- app/handler/system/list.go | 11 ++++++++++- app/views/system/list.html | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/handler/system/list.go b/app/handler/system/list.go index 4690475..7a98171 100644 --- a/app/handler/system/list.go +++ b/app/handler/system/list.go @@ -1,10 +1,12 @@ package system import ( + "cmp" "encoding/json" "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" + "slices" ) func List(c *fiber.Ctx) error { @@ -24,9 +26,16 @@ func List(c *fiber.Ctx) error { if err != nil { return err } + machinesOfSystem, err := db.GetMachinesBySystem(system.Id) + if err != nil { + return err + } + system.MachineCount = len(machinesOfSystem) Systems = append(Systems, system) } - + slices.SortFunc(Systems, func(a, b structs.System) int { + return cmp.Compare(b.MachineCount, a.MachineCount) + }) defer db.Close() return c.Render("app/views/system/list", fiber.Map{ diff --git a/app/views/system/list.html b/app/views/system/list.html index 0fbc99c..5f71775 100644 --- a/app/views/system/list.html +++ b/app/views/system/list.html @@ -1,8 +1,9 @@ {{ template "header" . }} - +
+ @@ -11,6 +12,7 @@ {{range .Systems}} +
NewMachines LTS EOL
{{.Name}}{{ .MachineCount }} {{ if .LTS }} True @@ -23,4 +25,7 @@ {{end}}
+ {{ template "footer" . }} From 147577d0dd75e29e3c061fad69f07a5ecf038983 Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 16:43:19 +0200 Subject: [PATCH 16/17] added filter by system.Id to machines --- app/handler/machine/filter/system.go | 22 ++++++++++++++++++++++ main.go | 1 + 2 files changed, 23 insertions(+) create mode 100644 app/handler/machine/filter/system.go diff --git a/app/handler/machine/filter/system.go b/app/handler/machine/filter/system.go new file mode 100644 index 0000000..03b2417 --- /dev/null +++ b/app/handler/machine/filter/system.go @@ -0,0 +1,22 @@ +package filter + +import ( + "github.com/gofiber/fiber/v2" + "github.com/typomedia/patchouli/app/store/boltdb" +) + +func System(c *fiber.Ctx) error { + systemId := c.Params("id") + db := boltdb.New() + + Machines, err := db.GetAllMachinesBySystemId(systemId) + if err != nil { + return err + } + + defer db.Close() + + return c.Render("app/views/machine/list", fiber.Map{ + "Machines": Machines, + }) +} diff --git a/main.go b/main.go index c84f569..e4a83a1 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ func main() { app.Get("/machine/new", machine.New) app.Get("/machine/edit/:id", machine.Edit) app.Get("/machine/filter/operator/:id", machineFilter.Operator) + app.Get("/machine/filter/system/:id", machineFilter.System) app.Post("/machine/save/:id", machine.Save) app.Get("/machine/update/list/:id", update.List) From c0b882a135e5d9369ee4b7a7d8bcb00ec6f3082f Mon Sep 17 00:00:00 2001 From: XotoX1337 Date: Fri, 4 Apr 2025 16:43:45 +0200 Subject: [PATCH 17/17] refactored retrieval of machines --- app/handler/machine/filter/operator.go | 24 +---- app/handler/machine/list.go | 32 ++----- app/handler/update/list.go | 2 +- app/store/boltdb/boltdb.go | 124 +++++++++++++++++-------- 4 files changed, 95 insertions(+), 87 deletions(-) diff --git a/app/handler/machine/filter/operator.go b/app/handler/machine/filter/operator.go index d8d0293..34ca66b 100644 --- a/app/handler/machine/filter/operator.go +++ b/app/handler/machine/filter/operator.go @@ -1,40 +1,22 @@ package filter import ( - "encoding/json" - "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" - "github.com/typomedia/patchouli/app/structs" ) func Operator(c *fiber.Ctx) error { - id := c.Params("id") + operatorId := c.Params("id") db := boltdb.New() - // set bucket - err := db.SetBucket("machine") + machines, err := db.GetAllMachinesByOperatorId(operatorId) if err != nil { return err } - machines, _ := db.GetAllByOperatorId(id, "machine") - - Machines := structs.Machines{} - - for _, v := range machines { - machine := structs.Machine{} - err = json.Unmarshal(v, &machine) - if err != nil { - return err - } - Machines = append(Machines, machine) - - } - defer db.Close() return c.Render("app/views/machine/list", fiber.Map{ - "Machines": Machines, + "Machines": machines, }) } diff --git a/app/handler/machine/list.go b/app/handler/machine/list.go index a9639f4..621c9f5 100644 --- a/app/handler/machine/list.go +++ b/app/handler/machine/list.go @@ -1,9 +1,6 @@ package machine import ( - "encoding/json" - "github.com/typomedia/patchouli/app" - "github.com/gofiber/fiber/v2" "github.com/typomedia/patchouli/app/store/boltdb" "github.com/typomedia/patchouli/app/structs" @@ -12,37 +9,20 @@ import ( func List(c *fiber.Ctx) error { db := boltdb.New() - config := app.GetApp().Config - - // set bucket - err := db.SetBucket("machine") - if err != nil { - return err - } - - machines, _ := db.GetAll("machine") + machines, _ := db.GetAllMachines(false) - Machines := structs.Machines{} - inactiveMachines := structs.Machines{} + var Machines, active, inactive structs.Machines - for _, v := range machines { - machine := structs.Machine{} - err = json.Unmarshal(v, &machine) - if err != nil { - return err - } - if machine.Interval == 0 { - machine.Interval = config.General.Interval - } + for _, machine := range machines { if machine.Inactive { - inactiveMachines = append(inactiveMachines, machine) + inactive = append(inactive, machine) } else { - Machines = append(Machines, machine) + active = append(active, machine) } } - Machines = append(Machines, inactiveMachines...) + Machines = append(active, inactive...) defer db.Close() diff --git a/app/handler/update/list.go b/app/handler/update/list.go index 4203ed5..4ed107c 100644 --- a/app/handler/update/list.go +++ b/app/handler/update/list.go @@ -22,7 +22,7 @@ func List(c *fiber.Ctx) error { log.Error(err) } - days, _ := db.GetAllByName(id, "history") + days, _ := db.GetAllUpdatesByMachineId(id) Updates := structs.Updates{} diff --git a/app/store/boltdb/boltdb.go b/app/store/boltdb/boltdb.go index af3e86f..38c2903 100644 --- a/app/store/boltdb/boltdb.go +++ b/app/store/boltdb/boltdb.go @@ -144,10 +144,10 @@ func (bolt *Bolt) GetAll(bucket string) ([][]byte, error) { return result, err } -func (bolt *Bolt) GetAllByName(id string, bucket string) ([][]byte, error) { +func (bolt *Bolt) GetAllUpdatesByMachineId(id string) ([][]byte, error) { var result [][]byte err := bolt.db.View(func(tx *bbolt.Tx) error { - bucket := tx.Bucket([]byte(bucket)) + bucket := tx.Bucket([]byte("history")) cursor := bucket.Cursor() for k, v := cursor.First(); k != nil; k, v = cursor.Next() { var update structs.Update @@ -164,54 +164,64 @@ func (bolt *Bolt) GetAllByName(id string, bucket string) ([][]byte, error) { return result, err } -func (bolt *Bolt) GetAllByOperatorId(id string, bucket string) ([][]byte, error) { - var result [][]byte - err := bolt.db.View(func(tx *bbolt.Tx) error { - bucket := tx.Bucket([]byte(bucket)) - cursor := bucket.Cursor() - for k, v := cursor.First(); k != nil; k, v = cursor.Next() { - var machine structs.Machine - err := json.Unmarshal(v, &machine) - if err != nil { - return err - } - if machine.Operator.Id == id { - result = append(result, v) - } +func (bolt *Bolt) GetAllMachinesByOperatorId(id string) (structs.Machines, error) { + var result structs.Machines + machines, err := bolt.GetAllMachines(false) + if err != nil { + return nil, err + } + for _, m := range machines { + if m.Operator.Id != id { + continue } - return nil - }) - return result, err + result = append(result, m) + } + return result, nil } -func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { - machines, _ := bolt.GetAll("machine") +func (bolt *Bolt) GetAllMachinesBySystemId(systemId string) (structs.Machines, error) { + var result structs.Machines + machines, err := bolt.GetAllMachines(false) + + for _, m := range machines { + if m.System.Id != systemId { + continue + } + result = append(result, m) + } + return result, err +} - Machines := structs.Machines{} +func (bolt *Bolt) GetAllMachines(onlyActive bool) (structs.Machines, error) { + var result structs.Machines + config, err := bolt.GetConfig() + machines, err := bolt.GetAll("machine") + if err != nil { + return nil, err + } for _, v := range machines { - machine := structs.Machine{} - err := json.Unmarshal(v, &machine) + m := structs.Machine{} + err = json.Unmarshal(v, &m) if err != nil { return nil, err } - - lastUpdate, _ := bolt.GetLastByName(machine.Id, "history") - - if lastUpdate != nil { - update := structs.Update{} - err = json.Unmarshal(lastUpdate, &update) - if err != nil { - return nil, err - } - machine.Update = update + if m.Inactive && onlyActive { + continue } - - if !machine.Inactive { - Machines = append(Machines, machine) + if m.Interval == 0 { + m.Interval = config.General.Interval } + result = append(result, m) + } + + return result, nil +} + +func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { + Machines, err := bolt.GetAllMachines(true) + if err != nil { + return nil, err } - // sort machines by oldest update first - sort.Sort(structs.ByDate(Machines)) config, err := bolt.GetConfig() if err != nil { @@ -221,6 +231,17 @@ func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { for i := range Machines { currentDate := time.Now() + lastUpdate, _ := bolt.GetLastByName(Machines[i].Id, "history") + + if lastUpdate != nil { + update := structs.Update{} + err = json.Unmarshal(lastUpdate, &update) + if err != nil { + return nil, err + } + Machines[i].Update = update + } + if Machines[i].Update.Date == "" { Machines[i].Update.Date = "0000-00-00" Machines[i].Status = "danger" @@ -254,6 +275,11 @@ func (bolt *Bolt) GetActiveMachines() (structs.Machines, error) { } + // sort machines by oldest update first + sort.Slice(Machines, func(i, j int) bool { + return Machines[i].Update.Date < Machines[j].Update.Date + }) + return Machines, nil } @@ -287,6 +313,26 @@ func (bolt *Bolt) GetOperatorById(id string) (structs.Operator, error) { return operator, nil } +func (bolt *Bolt) GetMachinesBySystem(systemId string) (structs.Machines, error) { + machines, err := bolt.GetAll("machine") + if err != nil { + return nil, err + } + machinesOfSystem := structs.Machines{} + for _, v := range machines { + machine := structs.Machine{} + err := json.Unmarshal(v, &machine) + if err != nil { + return nil, err + } + if machine.System.Id == systemId { + machinesOfSystem = append(machinesOfSystem, machine) + } + } + + return machinesOfSystem, nil +} + func (bolt *Bolt) Close() error { return bolt.db.Close() }