init project
commit
9d5b078475
|
@ -0,0 +1,11 @@
|
|||
[server]
|
||||
debug = true
|
||||
host = "127.0.0.1:3001"
|
||||
|
||||
[mail]
|
||||
host = "cmail02.mailhost24.de"
|
||||
port = "25"
|
||||
user = "no-reply@roese.dev"
|
||||
password = "mhtWMUn049o25xYTLUpfplX5Ze3jRfqh"
|
||||
from = "App <no-reply@roese.dev>"
|
||||
templatePath = "./templates/"
|
|
@ -0,0 +1,9 @@
|
|||
module git.umbach.dev/app-idea/mailer
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/gofiber/fiber/v2 v2.12.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
|
||||
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
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/gofiber/fiber/v2 v2.12.0 h1:R7FVMs9mtMREjfCzCioh2j8RHwhz0/H+X0rH6BpBkJ4=
|
||||
github.com/gofiber/fiber/v2 v2.12.0/go.mod h1:oZTLWqYnqpMMuF922SjGbsYZsdpE1MCfh416HNdweIM=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8=
|
||||
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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.26.0 h1:k5Tooi31zPG/g8yS6o2RffRO2C9B9Kah9SY8j/S7058=
|
||||
github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.umbach.dev/app-idea/mailer/modules/config"
|
||||
"git.umbach.dev/app-idea/mailer/modules/mailer"
|
||||
"git.umbach.dev/app-idea/mailer/routers/router"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := fiber.New()
|
||||
|
||||
config.LoadConfig()
|
||||
|
||||
cfg := &config.Cfg.Server
|
||||
|
||||
if cfg.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
mailer.InitMailer()
|
||||
|
||||
router.SetupRoutes(app)
|
||||
|
||||
app.Listen(cfg.Host)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
var Cfg Config
|
||||
|
||||
type Config struct {
|
||||
Server server
|
||||
Mail mail
|
||||
}
|
||||
|
||||
type server struct {
|
||||
Debug bool
|
||||
Host string
|
||||
}
|
||||
|
||||
type mail struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
From string
|
||||
TemplatePath string
|
||||
}
|
||||
|
||||
func LoadConfig() {
|
||||
toml.DecodeFile("./config.toml", &Cfg)
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package mailer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"git.umbach.dev/app-idea/mailer/modules/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var auth smtp.Auth
|
||||
var cfg = &config.Cfg.Mail
|
||||
|
||||
func InitMailer() {
|
||||
auth = smtp.PlainAuth("", cfg.User, cfg.Password, cfg.Host)
|
||||
|
||||
readTemplatesConfig()
|
||||
loadTemplateFiles()
|
||||
}
|
||||
|
||||
type Mail struct {
|
||||
To []string
|
||||
Subject string
|
||||
BodyTemplateId int
|
||||
LanguageId int
|
||||
BodyData interface{}
|
||||
}
|
||||
|
||||
func NewMail(to []string, bodyTemplateId int, languageId int, bodyData interface{}) {
|
||||
log.Infoln("new mail", bodyTemplateId, bodyData)
|
||||
|
||||
mail := Mail{
|
||||
To: to,
|
||||
Subject: "",
|
||||
BodyTemplateId: bodyTemplateId,
|
||||
LanguageId: languageId,
|
||||
BodyData: bodyData,
|
||||
}
|
||||
|
||||
body, err := mail.RenderTemplate()
|
||||
|
||||
log.Infoln("body", body, bodyData)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("error parsing", err)
|
||||
}
|
||||
|
||||
mail.Send(body)
|
||||
}
|
||||
|
||||
func (m *Mail) Send(body string) {
|
||||
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, auth, cfg.From, m.To, []byte(msg))
|
||||
|
||||
if err != nil {
|
||||
log.Warnln("smtp error:", err)
|
||||
} else {
|
||||
log.Debugln("send mail")
|
||||
}
|
||||
}
|
||||
|
||||
type Templates struct {
|
||||
Templates []Template `json:"templates"`
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Id int `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
Body string
|
||||
Languages []Languages `json:"languages"`
|
||||
}
|
||||
|
||||
type Languages struct {
|
||||
Id int `json:"id"`
|
||||
Subject string `json:"subject"`
|
||||
Texts map[string]interface{} `json:"texts"`
|
||||
}
|
||||
|
||||
var templates Templates
|
||||
|
||||
func readTemplatesConfig() {
|
||||
byteValue, err := ioutil.ReadFile("./templates/templates.json")
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(byteValue, &templates)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("failed unmarshal", err)
|
||||
}
|
||||
|
||||
log.Infoln("templates", templates)
|
||||
}
|
||||
|
||||
func loadTemplateFiles() {
|
||||
for i := 0; i < len(templates.Templates); i++ {
|
||||
log.Debugln("load template file id", templates.Templates[i].Id)
|
||||
|
||||
t := &templates.Templates[i]
|
||||
|
||||
data, err := ioutil.ReadFile(cfg.TemplatePath + t.FileName + ".html")
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("read file err", err)
|
||||
}
|
||||
|
||||
t.Body = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/28451675/it-takes-too-much-time-when-using-template-package-to-generate-a-dynamic-web-p
|
||||
|
||||
func (m *Mail) RenderTemplate() (string, error) {
|
||||
// define subject form config
|
||||
m.Subject = templates.Templates[0].Languages[0].Subject
|
||||
|
||||
body := templates.Templates[0].Body
|
||||
|
||||
// replace body %values% with values in templates config
|
||||
for i := 0; i < len(templates.Templates[0].Languages); i++ {
|
||||
for key, value := range templates.Templates[0].Languages[i].Texts {
|
||||
strKey := fmt.Sprintf("%v", key)
|
||||
strValue := fmt.Sprintf("%v", value)
|
||||
|
||||
log.Infoln("a", strKey, strValue)
|
||||
|
||||
body = strings.Replace(body, "%"+strKey+"%", strValue, -1)
|
||||
}
|
||||
}
|
||||
|
||||
t := template.Must(template.New("").Parse(body))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
log.Infoln("test")
|
||||
if err := t.Execute(buf, m.BodyData); err != nil {
|
||||
log.Fatalln("err execute", err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package mail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.umbach.dev/app-idea/mailer/modules/mailer"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type MailInput struct {
|
||||
Key string `json:"k"`
|
||||
Mail string `json:"m"`
|
||||
BodyTemplateId int `json:"t"`
|
||||
LanguageId int `json:"l"`
|
||||
BodyData *json.RawMessage `json:"d"`
|
||||
}
|
||||
|
||||
func SendMail(c *fiber.Ctx) error {
|
||||
var input MailInput
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
log.Infoln("bodyParser failed:", err)
|
||||
return c.SendStatus(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
var bodyData map[string]interface{}
|
||||
|
||||
if err := json.Unmarshal(*input.BodyData, &bodyData); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Infoln("input", input, bodyData)
|
||||
|
||||
mailer.NewMail([]string{"app@roese.dev"}, input.BodyTemplateId, input.LanguageId, bodyData)
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"git.umbach.dev/app-idea/mailer/routers/api/mail"
|
||||
)
|
||||
|
||||
func SetupRoutes(app *fiber.App) {
|
||||
app.Post("/send", mail.SendMail)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<!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>%text%</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>Or use this Url: {{.url}}</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"templates": [
|
||||
{
|
||||
"id": 0,
|
||||
"fileName": "accountActivation",
|
||||
"languages": [
|
||||
{
|
||||
"id": 0,
|
||||
"subject": "Konto aktivieren",
|
||||
"texts": {
|
||||
"header": "Hallo {{.name}},",
|
||||
"text": "Vielen Dank für das erstellen eines Kontos auf Roese.dev. Um dein Konto nutzen zu können, musst du es noch aktivieren.",
|
||||
"buttonText": "Account aktivieren"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"subject": "Account activation"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"fileName": "accountActivation",
|
||||
"languages": [
|
||||
{
|
||||
"id": 0,
|
||||
"subject": "Test"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue