From 998493688184454d43bce59649a19993511ea514 Mon Sep 17 00:00:00 2001 From: valentinkolb Date: Thu, 24 Oct 2024 19:05:17 +0200 Subject: [PATCH] feat(emailApi): added api to send emails --- emailApi/main.go | 102 +++++++++++++++++++++++++++++++++++++++++++ html/blankEmail.html | 97 ++++++++++++++++++++++++++++++++++++++++ ldapSync/tables.go | 7 +++ main/main.go | 8 +++- 4 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 emailApi/main.go create mode 100644 html/blankEmail.html diff --git a/emailApi/main.go b/emailApi/main.go new file mode 100644 index 0000000..12274ee --- /dev/null +++ b/emailApi/main.go @@ -0,0 +1,102 @@ +package emailApi + +import ( + "fmt" + "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/logger" + "github.com/pocketbase/pocketbase" + "github.com/pocketbase/pocketbase/core" + "github.com/pocketbase/pocketbase/models" + "github.com/pocketbase/pocketbase/tools/mailer" + "github.com/pocketbase/pocketbase/tools/template" + "net/mail" +) + +// sendEmailToUser sends an email notification to the user +func sendEmailToUser(app *pocketbase.PocketBase, registry *template.Registry, recipient *models.Record, sender *models.Record, emailRecord *models.Record) { + // check if recipient and message are set and recipient is not the sender + if recipient == nil || emailRecord == nil || sender == nil { + return + } + + recipientEmail := recipient.GetString("email") + senderEmail := sender.GetString("email") + + // render email template + html, err := registry.LoadFiles( + "html/emailNotification.html", + ).Render(map[string]any{ + "APP_URL": app.Settings().Meta.AppUrl, + "SENDER": sender.Username(), + "SENDER_EMAIL": senderEmail, + "CONTENT": emailRecord.GetString("content"), + }) + if err != nil { + currentErrors := emailRecord.GetString("errors") + emailRecord.Set("errors", fmt.Sprintf("%s\nThe email was not sent to the user '%s' due to an error.", currentErrors, recipient.Username())) + logger.LogErrorF("Error rendering email notification to recipient with username '%s': %v", recipient.GetString("username"), err) + return + } + + subject := emailRecord.GetString("subject") + headers := make(map[string]string) + headers["Reply-To"] = senderEmail + + // send email + email := &mailer.Message{ + From: mail.Address{ + Address: app.Settings().Meta.SenderAddress, + Name: app.Settings().Meta.SenderName, + }, + To: []mail.Address{{Address: recipientEmail}}, + Subject: "[StuVe IT] " + subject, + HTML: html, + } + if err := app.NewMailClient().Send(email); err != nil { + currentErrors := emailRecord.GetString("errors") + emailRecord.Set("errors", fmt.Sprintf("%s\nThe email was not sent to the user '%s' due to an error.", currentErrors, recipient.Username())) + logger.LogErrorF("Error sending email notification to recipient with username '%s': %v", recipient.GetString("username"), err) + } +} + +func sendEmails(app *pocketbase.PocketBase, emailRecord *models.Record) { + registry := template.NewRegistry() + + // expand the createdMessageRecord to get recipient user and send email notification if recipient is set + if errs := app.Dao().ExpandRecord(emailRecord, []string{"recipients, sender"}, nil); len(errs) > 0 { + // return new error with all errors + logger.LogErrorF("Error expanding created email record: %v", errs) + emailRecord.Set("errors", fmt.Sprintf("The email was not sent to anyone due to an error.")) + return + } + + sender := emailRecord.ExpandedOne("sender") + recipients := emailRecord.ExpandedAll("recipients") + for _, recipient := range recipients { + sendEmailToUser(app, registry, recipient, sender, emailRecord) + } + + return +} + +// InitEmailApi initializes the email notifier +// +// the function sends an email notification after it was created +func InitEmailApi(app *pocketbase.PocketBase, e *core.ServeEvent) error { + + logger.LogInfoF("Adding email notifier") + + app.OnModelAfterCreate("emails").Add(func(e *core.ModelEvent) error { + + // get created message record + createdMessageRecord, err := app.Dao().FindRecordById("emails", e.Model.GetId()) + if err != nil { + return err + } + + go sendEmails(app, createdMessageRecord) + + return nil + }) + + return nil +} diff --git a/html/blankEmail.html b/html/blankEmail.html new file mode 100644 index 0000000..b2195af --- /dev/null +++ b/html/blankEmail.html @@ -0,0 +1,97 @@ + +
+
+

Hallo đź‘‹,

+

Du hast eine neue Nachricht von {{.SENDER_NAME}} ({{.SENDER_EMAIL}})

+
+
+

{{.CONTENT}}

+
+
+
+
+
+
+ +

+ StuVe-IT +

+
+
+
+ AGB der StuVe + • + Datenschutzerklärung + • + Impressum +
+
+
\ No newline at end of file diff --git a/ldapSync/tables.go b/ldapSync/tables.go index 0a3e482..5d02e9b 100644 --- a/ldapSync/tables.go +++ b/ldapSync/tables.go @@ -203,6 +203,7 @@ func createLDAPUsersTable(app *pocketbase.PocketBase) error { // createLDAPSyncLogsTable creates ldapSyncLogs table func createLDAPSyncLogsTable(app *pocketbase.PocketBase) error { + // create ldapSyncs table collection := &models.Collection{} @@ -228,6 +229,9 @@ func createLDAPSyncLogsTable(app *pocketbase.PocketBase) error { form.Schema.AddField(&schema.SchemaField{ Name: "userSyncErrors", + Options: &schema.JsonOptions{ + MaxSize: 2000000, + }, Type: schema.FieldTypeJson, }) @@ -248,6 +252,9 @@ func createLDAPSyncLogsTable(app *pocketbase.PocketBase) error { form.Schema.AddField(&schema.SchemaField{ Name: "groupSyncErrors", + Options: &schema.JsonOptions{ + MaxSize: 2000000, + }, Type: schema.FieldTypeJson, }) diff --git a/main/main.go b/main/main.go index a6d7ba7..1cf25a8 100644 --- a/main/main.go +++ b/main/main.go @@ -1,6 +1,7 @@ package main import ( + "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/emailApi" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/ldapApi" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/ldapSync" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/logger" @@ -15,9 +16,9 @@ import ( func main() { // load env - err := godotenv.Load(".env.local", ".env") + err := godotenv.Load(".env.local") if err != nil { - logger.LogInfoF("The file '.env.local' / '.env' could not be loaded.") + logger.LogInfoF("The file '.env.local' could not be loaded: %v", err) } // create app @@ -41,6 +42,9 @@ func main() { // setup messages email notifications app.OnBeforeServe().Add(func(e *core.ServeEvent) error { return messages.InitMessages(app, e) }) + // setup email api + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { return emailApi.InitEmailApi(app, e) }) + // start app if err := app.Start(); err != nil { log.Fatal(err)