Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(feedback-api):Sending feedback-mails #199

Merged
merged 10 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ APNS_P8_FILE_PATH=/secrets/AuthKey_XXXX.p8
SENTRY_DSN=

CAMPUS_API_TOKEN=

SMTP_PASSWORD=
SMTP_URL=
SMTP_USERNAME=
SMTP_PORT=
2 changes: 1 addition & 1 deletion client/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/TUM-Dev/Campus-Backend/client
go 1.21

require (
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922140200-2092d9ee26fb
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922221907-9ec2b923010a
github.com/sirupsen/logrus v1.9.3
google.golang.org/grpc v1.58.1
)
Expand Down
4 changes: 2 additions & 2 deletions client/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922140200-2092d9ee26fb h1:zCxXbWPCo1z6+CadxETHfjcGWlgQQN55S3voe2PLXsQ=
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922140200-2092d9ee26fb/go.mod h1:FIIdW5aglREN0ULXZXDQtvuBdbTMa/fCrKSTS8FYP7k=
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922221907-9ec2b923010a h1:t7ahAwtri1UztDL6t0kce0OOkblQVHdMIWrhHkuEelQ=
github.com/TUM-Dev/Campus-Backend/server v0.0.0-20230922221907-9ec2b923010a/go.mod h1:FIIdW5aglREN0ULXZXDQtvuBdbTMa/fCrKSTS8FYP7k=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ services:
- MensaCronDisabled=true
- OMDB_API_KEY=${OMDB_API_KEY}
- CAMPUS_API_TOKEN=${CAMPUS_API_TOKEN}
- SMTP_PASSWORD=${SMTP_PASSWORD}
- SMTP_URL=${SMTP_URL:-postout.lrz.de}
- SMTP_USERNAME=${SMTP_USERNAME:[email protected]}
- SMTP_PORT=${SMTP_PORT:-587}
volumes:
- backend-storage:/Storage
- ./apns_auth_key.p8:${APNS_P8_FILE_PATH}
Expand Down
6 changes: 5 additions & 1 deletion server/backend/cron/cronjobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
IOSActivityReset = "iosActivityReset"
NewExamResultsHook = "newExamResultsHook"
MovieType = "movie"
FeedbackEmail = "feedbackEmail"

/* MensaType = "mensa"
AlarmType = "alarm" */
Expand Down Expand Up @@ -59,7 +60,7 @@ func (c *CronService) Run() error {
var res []model.Crontab

c.db.Model(&model.Crontab{}).
Where("`interval` > 0 AND (lastRun+`interval`) < ? AND type IN (?, ?, ?, ?, ?, ?, ?, ?, ?)",
Where("`interval` > 0 AND (lastRun+`interval`) < ? AND type IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
time.Now().Unix(),
NewsType,
FileDownloadType,
Expand All @@ -70,6 +71,7 @@ func (c *CronService) Run() error {
IOSActivityReset,
NewExamResultsHook,
MovieType,
FeedbackEmail,
).
Scan(&res)

Expand Down Expand Up @@ -125,6 +127,8 @@ func (c *CronService) Run() error {
g.Go(func() error { return c.iosNotificationsCron() })
case IOSActivityReset:
g.Go(func() error { return c.iosActivityReset() })
case FeedbackEmail:
g.Go(func() error { return c.feedbackEmailCron() })
}
}

Expand Down
42 changes: 42 additions & 0 deletions server/backend/cron/email_templates/feedback_body.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<h1>Feedback via TumCampusApp:</h1>
{{ if .Feedback.Valid -}}
<blockquote>
{{- .Feedback.String -}}
</blockquote>
{{- else -}}
<i>no feedback provided</i>
{{- end }}
<table>
<tr>
<th>Inforation type</th>
<th>Details</th>
</tr>
{{- if .Latitude.Valid }}
<tr>
<th>Nutzer-Standort</th>
<td>
<a href="https://www.google.com/maps/search/?api=1&query={{ .Latitude.Float64 }},{{ .Longitude.Float64 }}">
latitude: {{ .Latitude.Float64 }}, longitude: {{ .Longitude.Float64 }}
</a>
</td>
</tr>
{{- end }}
<tr>
<th>OS-Version</th>
<td>{{ if .OsVersion.Valid }}{{.OsVersion.String }}{{else}}unknown{{end}}</td>
</tr>
<tr>
<th>App-Version</th>
<td>{{ if .AppVersion.Valid }}{{.AppVersion.String }}{{else}}unknown{{end}}</td>
</tr>
</table>
{{- if .ImageCount }}
<h2>Fotos:</h2><br/>
<ol>
{{- range $val := iterate .ImageCount }}
<li>
<a href="https://app.tum.de/File/feedback/{{ $.Id }}/{{ $val }}.png">Foto {{ $val }}</a>
</li>
{{- end }}
</ol>
{{- end -}}
22 changes: 22 additions & 0 deletions server/backend/cron/email_templates/feedback_body.txt.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Feedback via TumCampusApp:

{{ if .Feedback.Valid }}
{{- .Feedback.String -}}
{{ else -}}
no feedback provided
{{- end }}

Metadata:
{{- if .Latitude.Valid }}
- Nutzer-Standort: {{ .Latitude.Float64 }},{{ .Longitude.Float64 }} (latitude,longitude)
https://www.google.com/maps/search/?api=1&query={{ .Latitude.Float64 }},{{ .Longitude.Float64 }}
{{- end }}
- OS-Version: {{ if .OsVersion.Valid }}{{.OsVersion.String }}{{else}}unknown{{end}}
- App-Version: {{ if .AppVersion.Valid }}{{.AppVersion.String }}{{else}}unknown{{end}}
{{- if .ImageCount }}

Photos:
{{- range $val := iterate .ImageCount }}
- Photo {{ $val }}: https://app.tum.de/File/feedback/{{ $.Id }}/{{ $val }}.png
{{- end -}}
{{- end -}}
139 changes: 139 additions & 0 deletions server/backend/cron/feedback_email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package cron

import (
"bytes"
"crypto/tls"
htmlTemplate "html/template"
"os"
"strconv"
textTemplate "text/template"
"time"

"github.com/TUM-Dev/Campus-Backend/server/model"
log "github.com/sirupsen/logrus"
"gopkg.in/gomail.v2"
kordianbruck marked this conversation as resolved.
Show resolved Hide resolved

_ "embed"
)

// iterate is a template helper to make counting possible
func iterate(count int32) []int32 {
var items []int32
var i int32
for i = 0; i < count; i++ {
items = append(items, i)
}
return items
}

//go:embed email_templates/feedback_body.gohtml
var htmlFeedbackBody string

//go:embed email_templates/feedback_body.txt.tmpl
var txtFeedbackBody string
CommanderStorm marked this conversation as resolved.
Show resolved Hide resolved

func parseTemplates() (*htmlTemplate.Template, *textTemplate.Template, error) {
funcMap := textTemplate.FuncMap{"iterate": iterate}
parsedHtmlBody, err := htmlTemplate.New("htmlFeedbackBody").Funcs(funcMap).Parse(htmlFeedbackBody)
if err != nil {
return nil, nil, err
}
parsedTxtBody, err := textTemplate.New("txtFeedbackBody").Funcs(funcMap).Parse(txtFeedbackBody)
if err != nil {
return nil, nil, err
}
return parsedHtmlBody, parsedTxtBody, nil

}

type MailHeaders struct {
From string
To string
ReplyTo string //optional
Timestamp time.Time
Subject string
}

func messageWithHeaders(feedback *model.Feedback) *gomail.Message {
m := gomail.NewMessage()
// From
m.SetAddressHeader("From", os.Getenv("SMTP_USERNAME"), "TUM Campus App")
// To
if feedback.Receiver.Valid {
m.SetHeader("To", feedback.Receiver.String)
} else {
m.SetHeader("To", "[email protected]")
}
// ReplyTo
if feedback.ReplyTo.Valid {
m.SetHeader("Reply-To", feedback.ReplyTo.String)
}
// Timestamp
if feedback.Timestamp.Valid {
m.SetDateHeader("Date", feedback.Timestamp.Time)
} else {
m.SetDateHeader("Date", time.Now())
}
// Subject
m.SetHeader("Subject", "Feedback via Tum Campus App")
return m
}

func generateTemplatedMail(parsedHtmlBody *htmlTemplate.Template, parsedTxtBody *textTemplate.Template, feedback *model.Feedback) (string, string, error) {
var htmlBodyBuffer bytes.Buffer
if err := parsedHtmlBody.Execute(&htmlBodyBuffer, feedback); err != nil {
return "", "", err
}
var txtBodyBuffer bytes.Buffer
if err := parsedTxtBody.Execute(&txtBodyBuffer, feedback); err != nil {
return "", "", err
}
return htmlBodyBuffer.String(), txtBodyBuffer.String(), nil
}

func (c *CronService) feedbackEmailCron() error {

var results []model.Feedback
if err := c.db.Find(&results, "processed = false").Scan(&results).Error; err != nil {
log.WithError(err).Fatal("could not get unprocessed feedback")
return err
}
parsedHtmlBody, parsedTxtBody, err := parseTemplates()
if err != nil {
log.WithError(err).Fatal("could not parse email templates")
return err
}

smtpPort, err := strconv.Atoi(os.Getenv("SMTP_PORT"))
if err != nil {
log.WithError(err).Fatal("SMTP_PORT is not an integer")
return err
}
d := gomail.NewDialer(os.Getenv("SMTP_URL"), smtpPort, os.Getenv("SMTP_USERNAME"), os.Getenv("SMTP_PASSWORD"))
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
CommanderStorm marked this conversation as resolved.
Show resolved Hide resolved
for i, feedback := range results {
m := messageWithHeaders(&feedback)

// attach a body
htmlBodyBuffer, txtBodyBuffer, err := generateTemplatedMail(parsedHtmlBody, parsedTxtBody, &feedback)
if err != nil {
log.WithError(err).Error("Could not template mail body")
return err
}
m.SetBody("text/plain", txtBodyBuffer)
m.AddAlternative("text/html", htmlBodyBuffer)

// send mail
if err := d.DialAndSend(m); err != nil {
log.WithError(err).Error("could not send mail")
continue
}
log.Tracef("sending feedback %d to %v successfull", i, feedback.Receiver)

// prevent the message being send the next time around
if err := c.db.Find(model.Feedback{}, "id = ?", feedback.Id).Update("processed", "true").Error; err != nil {
log.WithError(err).Error("could not prevent mail from being send again")
}
}
return nil
}
Loading