You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
2 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"path"
|
||
|
"text/template"
|
||
|
|
||
|
"git.cacert.org/cacert-boardvoting/internal"
|
||
|
"git.cacert.org/cacert-boardvoting/internal/models"
|
||
|
"github.com/Masterminds/sprig/v3"
|
||
|
"gopkg.in/mail.v2"
|
||
|
)
|
||
|
|
||
|
type headerData struct {
|
||
|
name string
|
||
|
value []string
|
||
|
}
|
||
|
|
||
|
type recipientData struct {
|
||
|
field, address, name string
|
||
|
}
|
||
|
|
||
|
type NotificationContent struct {
|
||
|
template string
|
||
|
data interface{}
|
||
|
subject string
|
||
|
headers []headerData
|
||
|
recipients []recipientData
|
||
|
}
|
||
|
|
||
|
type NotificationMail interface {
|
||
|
GetNotificationContent() *NotificationContent
|
||
|
}
|
||
|
|
||
|
type MailNotifier struct {
|
||
|
notifyChannel chan NotificationMail
|
||
|
senderAddress string
|
||
|
dialer *mail.Dialer
|
||
|
}
|
||
|
|
||
|
func (app *application) NewMailNotifier() {
|
||
|
app.mailNotifier = &MailNotifier{
|
||
|
notifyChannel: make(chan NotificationMail, 1),
|
||
|
senderAddress: app.mailConfig.NotificationSenderAddress,
|
||
|
dialer: mail.NewDialer(app.mailConfig.SMTPHost, app.mailConfig.SMTPPort, "", ""),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (app *application) StartMailNotifier() {
|
||
|
app.infoLog.Print("Launching mail notifier")
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case notification := <-app.mailNotifier.notifyChannel:
|
||
|
content := notification.GetNotificationContent()
|
||
|
|
||
|
mailText, err := content.buildMail(app.baseURL)
|
||
|
if err != nil {
|
||
|
app.errorLog.Printf("building mail failed: %v", err)
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
m := mail.NewMessage()
|
||
|
m.SetAddressHeader("From", app.mailNotifier.senderAddress, "CAcert board voting system")
|
||
|
|
||
|
for _, recipient := range content.recipients {
|
||
|
m.SetAddressHeader(recipient.field, recipient.address, recipient.name)
|
||
|
}
|
||
|
|
||
|
m.SetHeader("Subject", content.subject)
|
||
|
|
||
|
for _, header := range content.headers {
|
||
|
m.SetHeader(header.name, header.value...)
|
||
|
}
|
||
|
|
||
|
m.SetBody("text/plain", mailText.String())
|
||
|
|
||
|
if err = app.mailNotifier.dialer.DialAndSend(m); err != nil {
|
||
|
app.errorLog.Printf("sending mail failed: %v", err)
|
||
|
}
|
||
|
case <-app.ctx.Done():
|
||
|
app.infoLog.Print("ending mail notifier")
|
||
|
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (n *NotificationContent) buildMail(baseUrl string) (fmt.Stringer, error) {
|
||
|
b, err := internal.MailTemplates.ReadFile(
|
||
|
fmt.Sprintf(path.Join("mailtemplates", n.template)),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("could not read mail template %s: %w", n.template, err)
|
||
|
}
|
||
|
|
||
|
t, err := template.New(n.template).Funcs(sprig.GenericFuncMap()).Parse(string(b))
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("could not parse mail template %s: %w", n.template, err)
|
||
|
}
|
||
|
|
||
|
data := struct {
|
||
|
Data any
|
||
|
BaseURL string
|
||
|
}{Data: n.data, BaseURL: baseUrl}
|
||
|
|
||
|
mailText := bytes.NewBuffer(make([]byte, 0))
|
||
|
if err = t.Execute(mailText, data); err != nil {
|
||
|
return nil, fmt.Errorf(
|
||
|
"failed to execute template %s with context %v: %w", n.template, n.data, err)
|
||
|
}
|
||
|
|
||
|
return mailText, nil
|
||
|
}
|
||
|
|
||
|
type RemindVoterNotification struct {
|
||
|
voter models.Voter
|
||
|
decisions []models.Decision
|
||
|
}
|
||
|
|
||
|
func (r RemindVoterNotification) GetNotificationContent() *NotificationContent {
|
||
|
return &NotificationContent{
|
||
|
template: "remind_voter_mail.txt",
|
||
|
data: struct {
|
||
|
Decisions []models.Decision
|
||
|
Name string
|
||
|
}{Decisions: r.decisions, Name: r.voter.Name},
|
||
|
subject: "Outstanding CAcert board votes",
|
||
|
recipients: []recipientData{{"To", r.voter.Reminder, r.voter.Name}},
|
||
|
}
|
||
|
}
|