diff --git a/Makefile b/Makefile index 937b7dc..0099869 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ build: cmds cmds: go build -o bin/fgen ./tools/cmd/fgen + go build -o bin/qmgr ./tools/cmd/qmgr + go build -o bin/mailer ./tools/cmd/mailer site: go tool ssgod diff --git a/data/models.go b/data/models.go index 89c668a..93de3c3 100644 --- a/data/models.go +++ b/data/models.go @@ -31,3 +31,12 @@ func Models() map[string]reflect.Type { "null_example": reflect.TypeFor[NullExample](), } } + +type EmailMessage struct { + To string + From string + Subject string + Text string + HTML string +} + diff --git a/features/email/api.go b/features/email/api.go new file mode 100644 index 0000000..6e56ec5 --- /dev/null +++ b/features/email/api.go @@ -0,0 +1,15 @@ +package features_email + +import ( + "github.com/gofiber/fiber/v2" +) + +func GetApiExample(c *fiber.Ctx) error { + tables := []string{"test", "that"} + + return c.JSON(tables) +} + +func SetupApi(app *fiber.App) { + app.Get("/api/email/example", GetApiExample) +} diff --git a/features/email/db.go b/features/email/db.go new file mode 100644 index 0000000..15da825 --- /dev/null +++ b/features/email/db.go @@ -0,0 +1,9 @@ +package features_email + +import ( +// "MY/webapp/data" +// _ "github.com/mattn/go-sqlite3" +// sq "github.com/Masterminds/squirrel" +) + + diff --git a/features/email/init.go b/features/email/init.go new file mode 100644 index 0000000..9911a51 --- /dev/null +++ b/features/email/init.go @@ -0,0 +1,10 @@ +package features_email + +import ( + "github.com/gofiber/fiber/v2" +) + +func Setup(app *fiber.App) { + SetupApi(app) + SetupViews(app) +} diff --git a/features/email/views.go b/features/email/views.go new file mode 100644 index 0000000..bde12eb --- /dev/null +++ b/features/email/views.go @@ -0,0 +1,11 @@ +package features_email + +import ( + "github.com/gofiber/fiber/v2" + . "MY/webapp/common" +) + +func SetupViews(app *fiber.App) { + err := ConfigViews(app, "views/email") + if err != nil { panic(err) } +} diff --git a/features/init.go b/features/init.go index 0948953..921f5ae 100644 --- a/features/init.go +++ b/features/init.go @@ -2,7 +2,9 @@ package features import ( "github.com/gofiber/fiber/v2" + email "MY/webapp/features/email" ) func Setup(app *fiber.App) { + email.Setup(app) } diff --git a/go.mod b/go.mod index 9f3cefa..71953ea 100644 --- a/go.mod +++ b/go.mod @@ -12,18 +12,21 @@ require ( github.com/guregu/null/v6 v6.0.0 github.com/jmoiron/sqlx v1.4.0 github.com/mattn/go-sqlite3 v1.14.32 + github.com/redis/go-redis/v9 v9.17.2 github.com/stretchr/testify v1.11.1 + github.com/wneessen/go-mail v0.7.2 golang.org/x/crypto v0.43.0 - golang.org/x/net v0.46.0 ) require ( github.com/andybalholm/brotli v1.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d // indirect github.com/chromedp/sysutil v1.1.0 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.11 // indirect github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e // indirect diff --git a/go.sum b/go.sum index 0dcc0d6..559f455 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,12 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d h1:ZtA1sedVbEW7EW80Iz2GR3Ye6PwbJAJXjv7D74xG6HU= github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k= github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM= @@ -19,6 +25,8 @@ github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= @@ -89,6 +97,8 @@ github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhA github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= 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/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= +github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -99,14 +109,14 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok= github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= diff --git a/static/style.css b/static/style.css index 2f6c688..2e3d29d 100644 --- a/static/style.css +++ b/static/style.css @@ -307,9 +307,6 @@ .min-w-md { min-width: var(--container-md); } - .grow { - flex-grow: 1; - } .transform { transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); } @@ -398,11 +395,6 @@ } } } - .debug { - border-style: var(--tw-border-style) !important; - border-width: 1px !important; - border-color: var(--color-red-900) !important; - } .border-1 { border-style: var(--tw-border-style); border-width: 1px; diff --git a/tools/cmd/mailer/main.go b/tools/cmd/mailer/main.go new file mode 100644 index 0000000..248936d --- /dev/null +++ b/tools/cmd/mailer/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "github.com/wneessen/go-mail" + "fmt" + "context" + "github.com/redis/go-redis/v9" + "time" + "encoding/json" + "MY/webapp/data" +) + +func SendEmail(msg data.EmailMessage) { + email_msg := mail.NewMsg() + err := email_msg.From(msg.From) + if err != nil { panic(err) } + + err = email_msg.To(msg.To) + if err != nil { panic(err) } + + email_msg.Subject(msg.Subject) + email_msg.SetBodyString(mail.TypeTextPlain, msg.Text) + email_msg.SetBodyString(mail.TypeTextHTML, msg.HTML) + + client, err := mail.NewClient("localhost", + mail.WithPort(1025), + mail.WithTLSPolicy(mail.NoTLS)) + + if err != nil { panic(err) } + + err = client.DialAndSend(email_msg) + if err != nil { panic(err) } +} + +func LoadMessage(result string) data.EmailMessage { + var msg data.EmailMessage + + err := json.Unmarshal([]byte(result), &msg) + if err != nil { panic(err) } + + return msg +} + +func EmailListener(ctx context.Context) { + client := redis.NewClient(&redis.Options{ + Addr: "127.0.0.1:6379", + Password: "", + DB: 0, + }) + + for { + result, err := client.BRPop(ctx, 0, "queue").Result() + + if err != nil { panic(err) } + + fmt.Printf("received: %v\n", result) + + // NOTE: 0=queue name, 1=message + msg := LoadMessage(result[1]) + + SendEmail(msg) + + fmt.Println("SENT EMAIL") + } +} + +func main() { + ctx := context.Background() + + go EmailListener(ctx) + + time.Sleep(time.Second * 100) +} + + diff --git a/tools/cmd/qmgr/main.go b/tools/cmd/qmgr/main.go new file mode 100644 index 0000000..277dda2 --- /dev/null +++ b/tools/cmd/qmgr/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "github.com/redis/go-redis/v9" + "encoding/json" + "fmt" + "MY/webapp/data" +) + +func producer(ctx context.Context) { + client := redis.NewClient(&redis.Options{ + Addr: "127.0.0.1:6379", + Password: "", + DB: 0, + }) + + _, err := client.Ping(ctx).Result() + if err != nil { panic(err) } + + msg := data.EmailMessage{ + To: "tina.recip@example.com", + From: "toni.sender@example.com", + Subject: "This is my first mail.", + Text: fmt.Sprintf("Random number %v", 200), + HTML: fmt.Sprintf("