mailer
commit
2e6cbef3e1
|
@ -0,0 +1,14 @@
|
|||
debug: false
|
||||
rabbitmq:
|
||||
host: "127.0.0.1"
|
||||
username: "guest"
|
||||
password: "guest"
|
||||
mail:
|
||||
host: "127.0.0.1"
|
||||
username: "username"
|
||||
password: "password"
|
||||
port: ""
|
||||
from: "App <no-reply@xxx.xx>"
|
||||
templates:
|
||||
folderPath: "./templates/"
|
||||
configPath: "./templates/templates.json"
|
|
@ -0,0 +1,10 @@
|
|||
module clickandjoin.app/emailserver
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/rabbitmq/amqp091-go v1.5.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
|
@ -0,0 +1,46 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rabbitmq/amqp091-go v1.5.0 h1:VouyHPBu1CrKyJVfteGknGOGCzmOz0zcv/tONLkb7rg=
|
||||
github.com/rabbitmq/amqp091-go v1.5.0/go.mod h1:JsV0ofX5f1nwOGafb8L5rBItt9GyhfQfcJj+oyz0dGg=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,37 @@
|
|||
package mailer
|
||||
|
||||
import (
|
||||
"net/smtp"
|
||||
|
||||
"clickandjoin.app/emailserver/modules/cache"
|
||||
"clickandjoin.app/emailserver/modules/config"
|
||||
"clickandjoin.app/emailserver/modules/structs"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
cfg := config.Cfg.Mail
|
||||
|
||||
cache.SmtpAuth = smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
|
||||
|
||||
readTemplatesConfig()
|
||||
loadTemplateFiles()
|
||||
}
|
||||
|
||||
func NewMail(mail structs.Mail) error {
|
||||
logrus.Debugln("NEW MAIL")
|
||||
|
||||
body, err := mail.RenderTemplate()
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorln("Failed to render template, err:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mail.Send(body); err != nil {
|
||||
logrus.Errorln("Failed to send mail, err:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package mailer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"clickandjoin.app/emailserver/modules/cache"
|
||||
"clickandjoin.app/emailserver/modules/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func readTemplatesConfig() {
|
||||
logrus.Debugln("START READING TEMPLATE CONFIG")
|
||||
|
||||
byteValue, err := os.ReadFile(config.Cfg.Templates.ConfigPath)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorln("Failed to read config file, err:", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(byteValue, &cache.Templates)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorln("Failed to unmarshal templates config, err:", err)
|
||||
}
|
||||
|
||||
logrus.Debug("FINISHED READING TEMPLATE CONFIG")
|
||||
}
|
||||
|
||||
func loadTemplateFiles() {
|
||||
logrus.Debugln("STARTING IMPORTING TEMPLATE FILES")
|
||||
|
||||
for templateName, _ := range cache.Templates.Templates {
|
||||
data, err := os.ReadFile(config.Cfg.Templates.FolderPath + templateName + ".html")
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorln("Failed to read file, err:", err)
|
||||
}
|
||||
|
||||
cache.BodyTemplates[templateName] = data
|
||||
}
|
||||
|
||||
logrus.Debugln("FINISHED IMPORTING TEMPLATE FILES")
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"clickandjoin.app/emailserver/mailer"
|
||||
"clickandjoin.app/emailserver/modules/config"
|
||||
"clickandjoin.app/emailserver/modules/rabbitmq"
|
||||
"clickandjoin.app/emailserver/modules/structs"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
config.LoadConfig()
|
||||
|
||||
if config.Cfg.Debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
logrus.Println("Debug:", config.Cfg.Debug)
|
||||
|
||||
rabbitmq.Init()
|
||||
|
||||
mailer.Init()
|
||||
}
|
||||
|
||||
func main() {
|
||||
logrus.Println("test")
|
||||
|
||||
test := struct {
|
||||
Url string
|
||||
Username string
|
||||
ActivationCode string
|
||||
}{
|
||||
Url: "https://test.de",
|
||||
Username: "Ben",
|
||||
ActivationCode: "912 211",
|
||||
}
|
||||
|
||||
mailer.NewMail(structs.Mail{To: []string{"info@clickandjoin.de"}, TemplateId: "emailVerification", LanguageId: "de", BodyData: test})
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package cache
|
||||
|
||||
import "net/smtp"
|
||||
|
||||
type templates struct {
|
||||
Templates map[string]map[string]map[string]string
|
||||
}
|
||||
|
||||
var Templates templates
|
||||
var BodyTemplates = make(map[string][]byte)
|
||||
|
||||
var SmtpAuth smtp.Auth
|
|
@ -0,0 +1,48 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var Cfg Config
|
||||
|
||||
type Config struct {
|
||||
Debug bool
|
||||
RabbitMq RabbitMq
|
||||
Mail Mail
|
||||
Templates Templates
|
||||
}
|
||||
|
||||
type RabbitMq struct {
|
||||
Host string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type Mail struct {
|
||||
Host string
|
||||
Username string
|
||||
Password string
|
||||
Port string
|
||||
From string
|
||||
}
|
||||
|
||||
type Templates struct {
|
||||
FolderPath string
|
||||
ConfigPath string
|
||||
}
|
||||
|
||||
func LoadConfig() {
|
||||
data, err := os.ReadFile("config.yml")
|
||||
|
||||
if err != nil {
|
||||
logrus.Fatalln("Failed to read config file, err:", err)
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(data, &Cfg); err != nil {
|
||||
logrus.Fatalln("Failed to unmarshal config file, err:", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package rabbitmq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"clickandjoin.app/emailserver/modules/config"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var Conn *amqp.Connection
|
||||
var Channel *amqp.Channel
|
||||
|
||||
const queueMails = "cnj.mails"
|
||||
|
||||
func getConnectionString() string {
|
||||
cfg := &config.Cfg.RabbitMq
|
||||
|
||||
return fmt.Sprintf("amqp://%s:%s@%s/", cfg.Username, cfg.Password, cfg.Host)
|
||||
}
|
||||
|
||||
func Init() {
|
||||
conn, err := amqp.Dial(getConnectionString())
|
||||
|
||||
if err != nil {
|
||||
logrus.Fatalln("RabbitMQ connection failed, err:", err)
|
||||
}
|
||||
|
||||
ch, err := conn.Channel()
|
||||
|
||||
if err != nil {
|
||||
logrus.Fatalln(err)
|
||||
}
|
||||
|
||||
Channel = ch
|
||||
|
||||
err = ch.Qos(
|
||||
1, // prefetch count
|
||||
0, // prefetch size
|
||||
false, // global
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logrus.Fatalln("Failed to set Qos, err:", err)
|
||||
}
|
||||
|
||||
msgs, err := ch.Consume(
|
||||
queueMails, // queue
|
||||
"", // consumer
|
||||
false, // auto-ack
|
||||
false, // exclusive
|
||||
false, // no-local
|
||||
false, // no-wait
|
||||
nil, // args
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logrus.Fatalln("Failed to consume mails, err:", err)
|
||||
}
|
||||
|
||||
var forever chan struct{}
|
||||
|
||||
go func() {
|
||||
for d := range msgs {
|
||||
logrus.Printf("Received a message: %s", d.Body)
|
||||
}
|
||||
}()
|
||||
|
||||
<-forever
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"clickandjoin.app/emailserver/modules/cache"
|
||||
"clickandjoin.app/emailserver/modules/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Mail struct {
|
||||
To []string
|
||||
Subject string
|
||||
TemplateId string
|
||||
LanguageId string
|
||||
BodyData interface{}
|
||||
}
|
||||
|
||||
func (m *Mail) Send(body string) error {
|
||||
cfg := config.Cfg.Mail
|
||||
|
||||
msg := "From: " + cfg.From + "\n" +
|
||||
"To: " + strings.Join(m.To, ",") + "\n" +
|
||||
"Subject: " + m.Subject + "\n" +
|
||||
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" +
|
||||
body
|
||||
|
||||
err := smtp.SendMail(cfg.Host+":"+cfg.Port, cache.SmtpAuth, cfg.From, m.To, []byte(msg))
|
||||
|
||||
if err != nil {
|
||||
logrus.Warnln("smtp error:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugln("send mail", msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mail) RenderTemplate() (string, error) {
|
||||
body := cache.BodyTemplates[m.TemplateId]
|
||||
|
||||
for templateName, templateData := range cache.Templates.Templates {
|
||||
logrus.Debugln("RENDER TEMPLATE", templateName, templateData)
|
||||
|
||||
m.Subject = templateData["mailSubject"][m.LanguageId]
|
||||
|
||||
// replace body %values% with values in templates config
|
||||
for key, value := range templateData {
|
||||
logrus.Debugln("key", key, "value", value)
|
||||
|
||||
if key == "mailSubject" {
|
||||
continue
|
||||
}
|
||||
|
||||
body = []byte(strings.Replace(string(body), "%"+key+"%", value[m.LanguageId], -1))
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Println("body", string(body))
|
||||
|
||||
t := template.Must(template.New("").Parse(string(body)))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
logrus.Println("bodyData", m.BodyData)
|
||||
|
||||
if err := t.Execute(buf, m.BodyData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// define subject from config
|
||||
/*
|
||||
m.Subject = templates.Templates[m.TemplateId].Languages[m.LanguageId].Subject
|
||||
|
||||
body := templates.Templates[m.TemplateId].Body
|
||||
|
||||
// replace body %values% with values in templates config
|
||||
for key, value := range templates.Templates[m.TemplateId].Languages[m.LanguageId].Texts {
|
||||
strKey := fmt.Sprintf("%v", key)
|
||||
strValue := fmt.Sprintf("%v", value)
|
||||
|
||||
logrus.Infoln("a", strKey, strValue)
|
||||
|
||||
body = strings.Replace(body, "%"+strKey+"%", strValue, -1)
|
||||
}
|
||||
|
||||
t := template.Must(template.New("").Parse(body))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := t.Execute(buf, m.BodyData); err != nil {
|
||||
logrus.Fatalln("Error executing body data", err)
|
||||
} */
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package structs
|
||||
|
||||
type RabbitMqMessage struct {
|
||||
Cmd int
|
||||
Rec string
|
||||
Body any
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<body>
|
||||
<h1>%header%</h1>
|
||||
<p>%informationText%</p>
|
||||
<p>{{.ActivationCode}}</p>
|
||||
|
||||
<a
|
||||
href="{{.Url}}"
|
||||
style="
|
||||
padding: 8px 20px;
|
||||
background-color: #4b72fa;
|
||||
color: #fff;
|
||||
font-weight: bolder;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
margin: 20px 0px;
|
||||
margin-right: 20px;
|
||||
text-decoration: none;
|
||||
"
|
||||
>%buttonText%</a
|
||||
>
|
||||
|
||||
<p>%alternativeUrl% <a href="{{.Url}}">{{.Url}}</a></p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"templates": {
|
||||
"emailVerification": {
|
||||
"mailSubject": {
|
||||
"en": "Your email confirmation code",
|
||||
"de": "Ihr E-Mail-Bestätigungscode"
|
||||
},
|
||||
"header": {
|
||||
"en": "Hello {{.Username}}",
|
||||
"de": "Hallo {{.Username}},"
|
||||
},
|
||||
"informationText": {
|
||||
"en": "to confirm your identity we need to verify your email.",
|
||||
"de": "um deine Identität zu bestätigen müssen wir deine E-Mail verifizieren."
|
||||
},
|
||||
"buttonText": {
|
||||
"en": "Verify email",
|
||||
"de": "E-Mail verifizieren"
|
||||
},
|
||||
"alternativeUrl": {
|
||||
"en": "Alternatively you can click on the link here",
|
||||
"de": "Alternativ kannst du auch hier auf den Link klicken"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue