feat(emailApi): added api to send emails
Build and Push Docker image / build-and-push (push) Successful in 3m44s
Details
Build and Push Docker image / build-and-push (push) Successful in 3m44s
Details
This commit is contained in:
parent
c2f5680dff
commit
9984936881
|
@ -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
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<style>
|
||||
.logo {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px 20px 15px 20px;
|
||||
border-radius: 4px;
|
||||
background-color: #f1f3f5;
|
||||
}
|
||||
|
||||
.container > h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.blue-text {
|
||||
color: #339af0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
color: #fff;
|
||||
background-color: #339af0;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background-color: #228be6;
|
||||
}
|
||||
</style>
|
||||
<div class="stack">
|
||||
<div class="container">
|
||||
<h1 class="blue-text">Hallo 👋,</h1>
|
||||
<p>Du hast eine neue Nachricht von {{.SENDER_NAME}} ({{.SENDER_EMAIL}})</p>
|
||||
<br/>
|
||||
<div>
|
||||
<p>{{.CONTENT}}</p>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="group">
|
||||
<svg class="logo" width="100%" height="100%" viewBox="0 0 100 100" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.02041,0,0,1.02041,-197.959,0)">
|
||||
<rect id="Artboard1" x="194" y="0" width="98" height="98" style="fill:none;"/>
|
||||
<g id="Artboard11">
|
||||
<g transform="matrix(0.129488,0,0,0.128633,216.823,22.7241)">
|
||||
<g transform="matrix(7.56828,0,0,7.61857,-176.258,-188.086)">
|
||||
<path d="M95.493,95.155L4.5,95.155C4.5,95.155 4.726,76.243 5.56,72.26C5.961,70.348 5.798,55.477 5.798,55.477C5.798,55.477 5.802,55.187 5.8,55.048C5.795,54.633 6.268,53.615 6.72,53.294C7.011,53.086 7.343,52.821 7.343,52.419C7.345,49.181 7.341,34.535 7.341,34.535L7.339,34.302C7.329,33.742 7.68,32.975 8.093,32.614C8.55,32.214 9.207,31.923 9.441,31.656C9.589,31.487 9.709,31.294 9.815,30.999C9.9,30.761 16.837,4.845 16.837,4.845C16.837,4.845 23.776,30.762 23.861,30.999C23.967,31.294 24.087,31.487 24.234,31.656C24.469,31.923 25.126,32.214 25.583,32.614C25.996,32.975 26.347,33.742 26.337,34.302L26.334,34.535C26.334,34.535 26.331,49.181 26.333,52.419C26.333,52.821 26.665,53.086 26.956,53.294C27.408,53.615 27.88,54.633 27.876,55.048C27.874,55.187 27.878,55.477 27.878,55.477L27.863,65.838L73.37,65.833L76.83,49.964L78.56,57.898L80.294,49.942L83.757,65.832L83.757,71.587L90.119,71.584C90.119,71.584 91.628,71.592 91.636,71.588C92.215,71.34 94.358,74.803 94.912,76.282C95.606,78.131 95.493,95.155 95.493,95.155Z"
|
||||
style="fill:none;"/>
|
||||
</g>
|
||||
<g transform="matrix(7.56828,0,0,7.61857,-176.28,-188.086)">
|
||||
<path d="M94.912,76.282C95.606,78.131 95.5,98.155 95.5,98.155L4.5,98.155C4.5,98.155 4.726,76.243 5.56,72.26C5.961,70.348 5.798,55.477 5.798,55.477C5.798,55.477 5.802,55.187 5.8,55.048C5.795,54.633 6.268,53.615 6.72,53.294C7.011,53.086 7.343,52.821 7.343,52.419C7.345,49.181 7.341,34.535 7.341,34.535L7.339,34.302C7.329,33.742 7.68,32.975 8.093,32.614C8.55,32.214 9.207,31.923 9.441,31.656C9.589,31.487 9.709,31.294 9.815,30.999C9.9,30.761 16.837,4.845 16.837,4.845C16.837,4.845 23.776,30.762 23.861,30.999C23.967,31.294 24.087,31.487 24.234,31.656C24.469,31.923 25.126,32.214 25.583,32.614C25.996,32.975 26.347,33.742 26.337,34.302L26.334,34.535C26.334,34.535 26.331,49.181 26.333,52.419C26.333,52.821 26.665,53.086 26.956,53.294C27.408,53.615 27.88,54.633 27.876,55.048C27.874,55.187 27.878,55.477 27.878,55.477L27.863,65.838L73.37,65.833L76.83,49.964L78.56,57.898L80.294,49.942L83.757,65.832L83.757,71.587L90.119,71.584C90.119,71.584 91.628,71.592 91.636,71.588C92.215,71.34 94.358,74.803 94.912,76.282Z"
|
||||
style="fill:rgb(34,139,230);"/>
|
||||
</g>
|
||||
<g transform="matrix(3.31017,0,0,3.33216,-123.695,-80.204)">
|
||||
<path d="M47.904,125.072L82.509,185.184L13.3,185.184L47.904,125.072Z" style="fill:rgb(231,245,255);"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<h3 class="blue-text">
|
||||
StuVe-IT
|
||||
</h3>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="group">
|
||||
<a href="{{.APP_URL}}/legal/terms-and-conditions">AGB der StuVe</a>
|
||||
•
|
||||
<a href="{{.APP_URL}}/legal/privacy-policy">Datenschutzerklärung</a>
|
||||
•
|
||||
<a href="{{.APP_URL}}/legal/imprint">Impressum</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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,
|
||||
})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue