2021-04-05 23:57:57 +03:00
|
|
|
package mail
|
|
|
|
|
|
|
|
import (
|
2021-04-10 01:07:13 +03:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2022-12-01 12:57:07 +03:00
|
|
|
"github.com/muety/wakapi/helpers"
|
2021-04-30 15:07:14 +03:00
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
"github.com/muety/wakapi/routes"
|
2021-04-30 16:14:29 +03:00
|
|
|
"github.com/muety/wakapi/services"
|
2021-04-30 17:20:08 +03:00
|
|
|
"github.com/muety/wakapi/utils"
|
2021-08-08 13:33:04 +03:00
|
|
|
"github.com/muety/wakapi/views/mail"
|
2021-04-30 16:14:29 +03:00
|
|
|
"time"
|
2021-04-11 13:42:43 +03:00
|
|
|
|
|
|
|
conf "github.com/muety/wakapi/config"
|
2021-04-05 23:57:57 +03:00
|
|
|
)
|
|
|
|
|
2021-04-10 01:07:13 +03:00
|
|
|
const (
|
2021-08-07 00:28:03 +03:00
|
|
|
tplNamePasswordReset = "reset_password"
|
|
|
|
tplNameImportNotification = "import_finished"
|
|
|
|
tplNameWakatimeFailureNotification = "wakatime_connection_failure"
|
|
|
|
tplNameReport = "report"
|
2022-12-29 19:12:34 +03:00
|
|
|
tplNameSubscriptionNotification = "subscription_expiring"
|
2021-08-07 00:28:03 +03:00
|
|
|
subjectPasswordReset = "Wakapi - Password Reset"
|
|
|
|
subjectImportNotification = "Wakapi - Data Import Finished"
|
|
|
|
subjectWakatimeFailureNotification = "Wakapi - WakaTime Connection Failure"
|
|
|
|
subjectReport = "Wakapi - Report from %s"
|
2022-12-29 19:12:34 +03:00
|
|
|
subjectSubscriptionNotification = "Wakapi - Subscription expiring / expired"
|
2021-04-10 01:07:13 +03:00
|
|
|
)
|
|
|
|
|
2021-04-30 16:14:29 +03:00
|
|
|
type SendingService interface {
|
|
|
|
Send(*models.Mail) error
|
2021-04-10 11:48:06 +03:00
|
|
|
}
|
|
|
|
|
2021-04-30 16:14:29 +03:00
|
|
|
type MailService struct {
|
|
|
|
config *conf.Config
|
|
|
|
sendingService SendingService
|
2021-08-08 13:33:04 +03:00
|
|
|
templates utils.TemplateMap
|
2021-04-30 15:07:14 +03:00
|
|
|
}
|
|
|
|
|
2021-04-05 23:57:57 +03:00
|
|
|
func NewMailService() services.IMailService {
|
|
|
|
config := conf.Get()
|
2021-04-30 16:14:29 +03:00
|
|
|
|
|
|
|
var sendingService SendingService
|
|
|
|
sendingService = &NoopSendingService{}
|
|
|
|
|
2021-04-10 01:07:13 +03:00
|
|
|
if config.Mail.Enabled {
|
|
|
|
if config.Mail.Provider == conf.MailProviderMailWhale {
|
2021-04-30 16:14:29 +03:00
|
|
|
sendingService = NewMailWhaleSendingService(config.Mail.MailWhale)
|
2021-04-10 01:07:13 +03:00
|
|
|
} else if config.Mail.Provider == conf.MailProviderSmtp {
|
2021-04-30 16:14:29 +03:00
|
|
|
sendingService = NewSMTPSendingService(config.Mail.Smtp)
|
2021-04-10 01:07:13 +03:00
|
|
|
}
|
2021-04-05 23:57:57 +03:00
|
|
|
}
|
2021-04-30 16:14:29 +03:00
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
// Use local file system when in 'dev' environment, go embed file system otherwise
|
|
|
|
templateFs := conf.ChooseFS("views/mail", mail.TemplateFiles)
|
|
|
|
templates, err := utils.LoadTemplates(templateFs, routes.DefaultTemplateFuncs())
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &MailService{sendingService: sendingService, config: config, templates: templates}
|
2021-04-30 16:14:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MailService) SendPasswordReset(recipient *models.User, resetLink string) error {
|
2021-08-08 13:33:04 +03:00
|
|
|
tpl, err := m.getPasswordResetTemplate(PasswordResetTplData{ResetLink: resetLink})
|
2021-04-30 16:14:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(m.config.Mail.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectPasswordReset,
|
|
|
|
}
|
|
|
|
mail.WithHTML(tpl.String())
|
|
|
|
return m.sendingService.Send(mail)
|
|
|
|
}
|
|
|
|
|
2021-08-07 00:28:03 +03:00
|
|
|
func (m *MailService) SendWakatimeFailureNotification(recipient *models.User, numFailures int) error {
|
2021-08-08 13:33:04 +03:00
|
|
|
tpl, err := m.getWakatimeFailureNotificationTemplate(WakatimeFailureNotificationNotificationTplData{
|
2021-08-07 00:28:03 +03:00
|
|
|
PublicUrl: m.config.Server.PublicUrl,
|
|
|
|
NumFailures: numFailures,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(m.config.Mail.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectWakatimeFailureNotification,
|
|
|
|
}
|
|
|
|
mail.WithHTML(tpl.String())
|
|
|
|
return m.sendingService.Send(mail)
|
|
|
|
}
|
|
|
|
|
2021-04-30 16:14:29 +03:00
|
|
|
func (m *MailService) SendImportNotification(recipient *models.User, duration time.Duration, numHeartbeats int) error {
|
2021-08-08 13:33:04 +03:00
|
|
|
tpl, err := m.getImportNotificationTemplate(ImportNotificationTplData{
|
2021-04-30 16:14:29 +03:00
|
|
|
PublicUrl: m.config.Server.PublicUrl,
|
|
|
|
Duration: fmt.Sprintf("%.0f seconds", duration.Seconds()),
|
|
|
|
NumHeartbeats: numHeartbeats,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(m.config.Mail.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectImportNotification,
|
|
|
|
}
|
|
|
|
mail.WithHTML(tpl.String())
|
|
|
|
return m.sendingService.Send(mail)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *MailService) SendReport(recipient *models.User, report *models.Report) error {
|
2021-08-08 13:33:04 +03:00
|
|
|
tpl, err := m.getReportTemplate(ReportTplData{report})
|
2021-04-30 16:14:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(m.config.Mail.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
2022-12-01 12:57:07 +03:00
|
|
|
Subject: fmt.Sprintf(subjectReport, helpers.FormatDateHuman(time.Now().In(recipient.TZ()))),
|
2021-04-30 16:14:29 +03:00
|
|
|
}
|
|
|
|
mail.WithHTML(tpl.String())
|
|
|
|
return m.sendingService.Send(mail)
|
2021-04-05 23:57:57 +03:00
|
|
|
}
|
|
|
|
|
2022-12-29 19:12:34 +03:00
|
|
|
func (m *MailService) SendSubscriptionNotification(recipient *models.User, hasExpired bool) error {
|
|
|
|
tpl, err := m.getSubscriptionNotificationTemplate(SubscriptionNotificationTplData{
|
|
|
|
PublicUrl: m.config.Server.PublicUrl,
|
|
|
|
DataRetentionMonths: m.config.App.DataRetentionMonths,
|
|
|
|
HasExpired: hasExpired,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mail := &models.Mail{
|
|
|
|
From: models.MailAddress(m.config.Mail.Sender),
|
|
|
|
To: models.MailAddresses([]models.MailAddress{models.MailAddress(recipient.Email)}),
|
|
|
|
Subject: subjectSubscriptionNotification,
|
|
|
|
}
|
|
|
|
mail.WithHTML(tpl.String())
|
|
|
|
return m.sendingService.Send(mail)
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
func (m *MailService) getPasswordResetTemplate(data PasswordResetTplData) (*bytes.Buffer, error) {
|
2021-04-10 01:07:13 +03:00
|
|
|
var rendered bytes.Buffer
|
2021-08-08 13:33:04 +03:00
|
|
|
if err := m.templates[m.fmtName(tplNamePasswordReset)].Execute(&rendered, data); err != nil {
|
2021-04-10 01:07:13 +03:00
|
|
|
return nil, err
|
2021-08-07 00:28:03 +03:00
|
|
|
}
|
|
|
|
return &rendered, nil
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
func (m *MailService) getWakatimeFailureNotificationTemplate(data WakatimeFailureNotificationNotificationTplData) (*bytes.Buffer, error) {
|
2021-08-07 00:28:03 +03:00
|
|
|
var rendered bytes.Buffer
|
2021-08-08 13:33:04 +03:00
|
|
|
if err := m.templates[m.fmtName(tplNameWakatimeFailureNotification)].Execute(&rendered, data); err != nil {
|
2021-08-07 00:28:03 +03:00
|
|
|
return nil, err
|
2021-04-10 01:07:13 +03:00
|
|
|
}
|
|
|
|
return &rendered, nil
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
func (m *MailService) getImportNotificationTemplate(data ImportNotificationTplData) (*bytes.Buffer, error) {
|
2021-04-10 11:48:06 +03:00
|
|
|
var rendered bytes.Buffer
|
2021-08-08 13:33:04 +03:00
|
|
|
if err := m.templates[m.fmtName(tplNameImportNotification)].Execute(&rendered, data); err != nil {
|
2021-04-10 11:48:06 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &rendered, nil
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
func (m *MailService) getReportTemplate(data ReportTplData) (*bytes.Buffer, error) {
|
2021-04-30 15:07:14 +03:00
|
|
|
var rendered bytes.Buffer
|
2021-08-08 13:33:04 +03:00
|
|
|
if err := m.templates[m.fmtName(tplNameReport)].Execute(&rendered, data); err != nil {
|
2021-04-30 15:07:14 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &rendered, nil
|
|
|
|
}
|
|
|
|
|
2022-12-29 19:12:34 +03:00
|
|
|
func (m *MailService) getSubscriptionNotificationTemplate(data SubscriptionNotificationTplData) (*bytes.Buffer, error) {
|
|
|
|
var rendered bytes.Buffer
|
|
|
|
if err := m.templates[m.fmtName(tplNameSubscriptionNotification)].Execute(&rendered, data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &rendered, nil
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:33:04 +03:00
|
|
|
func (m *MailService) fmtName(name string) string {
|
|
|
|
return fmt.Sprintf("%s.tpl.html", name)
|
2021-04-05 23:57:57 +03:00
|
|
|
}
|