From 0f3da902dc5810dda9db23563e56e825daa53d1d Mon Sep 17 00:00:00 2001 From: schreifuchs Date: Sun, 24 Aug 2025 13:11:31 +0200 Subject: [PATCH] feat: send mail --- go.mod | 3 ++- go.sum | 8 +++++-- index.html | 8 +++---- mailer/model.go | 15 +++++++++++++ mailer/resource.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++ mailer/send.go | 33 +++++++++++++++++++++++++++++ main.go | 35 ++++++++++++++++++++++++++++--- pdf/resource.go | 7 ------- 8 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 mailer/model.go create mode 100644 mailer/resource.go create mode 100644 mailer/send.go diff --git a/go.mod b/go.mod index 3b2ac3d..ed621c4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.24.5 require ( code.gitea.io/sdk/gitea v0.21.0 github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a - github.com/google/uuid v1.6.0 github.com/jedib0t/go-pretty/v6 v6.6.8 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/starwalkn/gotenberg-go-client/v8 v8.11.0 @@ -16,9 +15,11 @@ require ( github.com/davidmz/go-pageant v1.0.2 // indirect github.com/go-fed/httpsig v1.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect ) diff --git a/go.sum b/go.sum index b66aa7d..fb3b102 100644 --- a/go.sum +++ b/go.sum @@ -10,12 +10,12 @@ github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A= github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -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/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc= github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -49,5 +49,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= 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/index.html b/index.html index 95779d2..a492e7c 100644 --- a/index.html +++ b/index.html @@ -224,7 +224,7 @@ article {

- Rechnung: 20 25082 32224 30630 09759 35659
+ Rechnung: 20 25082 41057 10271 08692 82635
Datum: 24.08.2025

@@ -392,7 +392,7 @@ article {

Referenz

-

20 25082 32224 30630 09759 35659

+

20 25082 41057 10271 08692 82635

Zahlbar durch

@@ -481,7 +481,7 @@ article {
- + @@ -592,7 +592,7 @@ article {

Referenz

-

20 25082 32224 30630 09759 35659

+

20 25082 41057 10271 08692 82635

Zahlbar durch

diff --git a/mailer/model.go b/mailer/model.go new file mode 100644 index 0000000..7b54898 --- /dev/null +++ b/mailer/model.go @@ -0,0 +1,15 @@ +package mailer + +import "io" + +type Mail struct { + TO string + Subject string + Body string + Attachments []Attachment +} +type Attachment struct { + Name string + MimeType string + Content io.Reader +} diff --git a/mailer/resource.go b/mailer/resource.go new file mode 100644 index 0000000..5b44583 --- /dev/null +++ b/mailer/resource.go @@ -0,0 +1,52 @@ +package mailer + +import ( + "crypto/tls" + "net/smtp" + + "github.com/jordan-wright/email" +) + +type Config struct { + SMTP SMTPConfig + From string +} + +type SMTPConfig struct { + Host string + Port string + User string + Password string +} + +type Service struct { + from string + pool *email.Pool + cfg Config +} + +func New(cfg Config) (s *Service, err error) { + p, err := email.NewPool( + cfg.SMTP.Host+":"+cfg.SMTP.Port, + 1, + smtp.PlainAuth( + "", + cfg.SMTP.User, + cfg.SMTP.Password, + cfg.SMTP.Host, + ), &tls.Config{ + InsecureSkipVerify: true, // set false in production + ServerName: cfg.SMTP.Host, // must match the SMTP host + }) + if err != nil { + return + } + + s = &Service{ + from: cfg.From, + pool: p, + cfg: cfg, + } + + return +} diff --git a/mailer/send.go b/mailer/send.go new file mode 100644 index 0000000..f359b03 --- /dev/null +++ b/mailer/send.go @@ -0,0 +1,33 @@ +package mailer + +import ( + "crypto/tls" + "net/smtp" + + "github.com/jordan-wright/email" +) + +func (s Service) Send(m Mail) (err error) { + e := email.NewEmail() + e.To = []string{m.TO} + e.Bcc = []string{"niklas@sunway.ch"} + e.From = s.from + e.Subject = m.Subject + e.Text = []byte(m.Body) + + for _, a := range m.Attachments { + e.Attach(a.Content, a.Name, a.MimeType) + } + err = e.SendWithTLS(s.cfg.SMTP.Host+":"+s.cfg.SMTP.Port, smtp.PlainAuth( + s.cfg.SMTP.User, + s.cfg.SMTP.User, + s.cfg.SMTP.Password, + s.cfg.SMTP.Host, + ), &tls.Config{ + ServerName: s.cfg.SMTP.Host, + }) + + // err = s.pool.Send(e, time.Second*5) + + return +} diff --git a/main.go b/main.go index 2b0b78f..faac9b3 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,12 @@ package main import ( - "io" "os" "time" "code.gitea.io/sdk/gitea" "git.schreifuchs.ch/lou-taylor/accounting/issue" + "git.schreifuchs.ch/lou-taylor/accounting/mailer" "git.schreifuchs.ch/lou-taylor/accounting/model" "git.schreifuchs.ch/lou-taylor/accounting/pdf" "git.schreifuchs.ch/lou-taylor/accounting/report" @@ -99,9 +99,38 @@ func main() { panic(err) } - ducument, err := pdfs.HtmlToPdf(html) + document, err := pdfs.HtmlToPdf(html) + if err != nil { + panic(err) + } - io.Copy(os.Stdout, ducument) + mlr, err := mailer.New(mailer.Config{ + SMTP: mailer.SMTPConfig{ + Host: "mail.your-server.de", + Port: "465", + User: "test@schreifuchs.ch", + Password: "xV27D1nj33dNz8B4", + }, + From: "test@schreifuchs.ch", + }) + if err != nil { + panic(err) + } + err = mlr.Send(mailer.Mail{ + TO: "kontakt@schreifuchs.ch", + Subject: "test", + Body: "Hallo", + Attachments: []mailer.Attachment{ + { + Name: "invoice.pdf", + MimeType: "pdf", + Content: document, + }, + }, + }) + if err != nil { + panic(err) + } } func Filter[T any](slice []T, ok func(T) bool) []T { diff --git a/pdf/resource.go b/pdf/resource.go index 76075e4..3240ccd 100644 --- a/pdf/resource.go +++ b/pdf/resource.go @@ -25,13 +25,6 @@ func (s Service) HtmlToPdf(html string) (pdf io.ReadCloser, err error) { } req := gotenberg.NewHTMLRequest(index) req.PaperSize(gotenberg.A4) - // req.Margins(gotenberg.PageMargins{ - // Top: 0.5, - // Bottom: 0.5, - // Left: 0.5, - // Right: 0.6, - // Unit: gotenberg.IN, - // }) req.Margins(gotenberg.NoMargins) // Skips the IDLE events for faster PDF conversion.