add: plaintext email; improved templating

This commit is contained in:
$(pass /github/name)
2024-08-28 11:35:43 +02:00
parent 00ca11d2e6
commit 6ac2b32a1f
10 changed files with 287 additions and 116 deletions

1
.gitignore vendored
View File

@@ -36,6 +36,7 @@ go.work
!*.go !*.go
!*.html !*.html
!*.tmpl
!*.css !*.css
!go.sum !go.sum
!go.mod !go.mod

View File

@@ -1,5 +1,7 @@
{ {
"WebsiteTitle": "My Carsharing Site",
"BaseURL": "https://domain.de", "BaseURL": "https://domain.de",
"Environment": "dev",
"db": { "db": {
"Path": "data/db.sqlite3" "Path": "data/db.sqlite3"
}, },
@@ -8,13 +10,13 @@
"User": "username", "User": "username",
"Password": "password", "Password": "password",
"Port": 465, "Port": 465,
"Mailtype": "html",
"AdminEmail": "admin@server.com" "AdminEmail": "admin@server.com"
}, },
"templates": { "templates": {
"MailPath": "templates/email", "MailPath": "templates/email",
"HTMLPath": "templates/html", "HTMLPath": "templates/html",
"StaticPath": "templates/css" "StaticPath": "templates/css",
"LogoURI": "/images/LOGO.png"
}, },
"auth": { "auth": {
"APIKey": "" "APIKey": ""

View File

@@ -33,7 +33,6 @@ type SMTPConfig struct {
Host string `json:"Host" envconfig:"SMTP_HOST"` Host string `json:"Host" envconfig:"SMTP_HOST"`
User string `json:"User" envconfig:"SMTP_USER"` User string `json:"User" envconfig:"SMTP_USER"`
Password string `json:"Password" envconfig:"SMTP_PASS"` Password string `json:"Password" envconfig:"SMTP_PASS"`
Mailtype string `json:"Mailtype" default:"html" envconfig:"MAIL_TYPE"`
AdminEmail string `json:"AdminEmail" envconfig:"ADMIN_MAIL"` AdminEmail string `json:"AdminEmail" envconfig:"ADMIN_MAIL"`
Port int `json:"Port" default:"465" envconfig:"SMTP_PORT"` Port int `json:"Port" default:"465" envconfig:"SMTP_PORT"`
} }
@@ -42,6 +41,7 @@ type TemplateConfig struct {
MailPath string `json:"MailPath" default:"templates/email" envconfig:"TEMPLATE_MAIL_PATH"` MailPath string `json:"MailPath" default:"templates/email" envconfig:"TEMPLATE_MAIL_PATH"`
HTMLPath string `json:"HTMLPath" default:"templates/html" envconfig:"TEMPLATE_HTML_PATH"` HTMLPath string `json:"HTMLPath" default:"templates/html" envconfig:"TEMPLATE_HTML_PATH"`
StaticPath string `json:"StaticPath" default:"templates/css" envconfig:"TEMPLATE_STATIC_PATH"` StaticPath string `json:"StaticPath" default:"templates/css" envconfig:"TEMPLATE_STATIC_PATH"`
LogoURI string `json:"LogoURI" envconfig:"LOGO_URI"`
} }
type RecipientsConfig struct { type RecipientsConfig struct {
@@ -60,6 +60,7 @@ type Config struct {
Templates TemplateConfig `json:"templates"` Templates TemplateConfig `json:"templates"`
Recipients RecipientsConfig `json:"recipients"` Recipients RecipientsConfig `json:"recipients"`
ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"` ConfigFilePath string `json:"config_file_path" envconfig:"CONFIG_FILE_PATH"`
WebsiteTitle string `json:"WebsiteTitle" envconfig:"WEBSITE_TITLE"`
BaseURL string `json:"BaseUrl" envconfig:"BASE_URL"` BaseURL string `json:"BaseUrl" envconfig:"BASE_URL"`
Env string `json:"Environment" default:"development" envconfig:"ENV"` Env string `json:"Environment" default:"development" envconfig:"ENV"`
DB DatabaseConfig `json:"db"` DB DatabaseConfig `json:"db"`
@@ -68,16 +69,17 @@ type Config struct {
} }
var ( var (
BaseURL string BaseURL string
CFGPath string WebsiteTitle string
CFG Config CFGPath string
Auth AuthenticationConfig CFG Config
DB DatabaseConfig Auth AuthenticationConfig
Templates TemplateConfig DB DatabaseConfig
SMTP SMTPConfig Templates TemplateConfig
Recipients RecipientsConfig SMTP SMTPConfig
Env string Recipients RecipientsConfig
Security SecurityConfig Env string
Security SecurityConfig
) )
var environmentOptions map[string]bool = map[string]bool{ var environmentOptions map[string]bool = map[string]bool{
"development": true, "development": true,
@@ -117,6 +119,7 @@ func LoadConfig() {
Recipients = CFG.Recipients Recipients = CFG.Recipients
Security = CFG.Security Security = CFG.Security
Env = CFG.Env Env = CFG.Env
WebsiteTitle = CFG.WebsiteTitle
logger.Info.Printf("Config loaded: %#v", CFG) logger.Info.Printf("Config loaded: %#v", CFG)
} }

View File

@@ -128,6 +128,12 @@ func checkWelcomeMail(message *utils.Email, user *models.User) error {
if !strings.Contains(message.Body, config.BaseURL) { if !strings.Contains(message.Body, config.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL) return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
} }
if !strings.Contains(message.Body, config.BaseURL+config.Templates.LogoURI) {
return fmt.Errorf("Logo Url (%v) has not been rendered in registration mail.", config.BaseURL+config.WebsiteTitle)
}
if !strings.Contains(message.Body, config.WebsiteTitle) {
return fmt.Errorf("Website title (%v) has not been rendered in registration mail.", config.WebsiteTitle)
}
return nil return nil
} }

View File

@@ -74,7 +74,7 @@ func (s *EmailService) SendVerificationEmail(user *models.User, token *string) e
} }
subject := constants.MailVerificationSubject subject := constants.MailVerificationSubject
body, err := ParseTemplate("mail_verification.html", data) body, err := ParseTemplate("mail_verification.tmpl", data)
if err != nil { if err != nil {
logger.Error.Print("Couldn't send verification mail") logger.Error.Print("Couldn't send verification mail")
return err return err
@@ -92,6 +92,8 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
BASEURL string BASEURL string
MembershipID int64 MembershipID int64
MembershipFee float32 MembershipFee float32
Logo string
WebsiteTitle string
RentalFee float32 RentalFee float32
}{ }{
Company: user.Company, Company: user.Company,
@@ -101,10 +103,12 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
MembershipFee: float32(user.Membership.SubscriptionModel.MonthlyFee), MembershipFee: float32(user.Membership.SubscriptionModel.MonthlyFee),
RentalFee: float32(user.Membership.SubscriptionModel.HourlyRate), RentalFee: float32(user.Membership.SubscriptionModel.HourlyRate),
BASEURL: config.BaseURL, BASEURL: config.BaseURL,
WebsiteTitle: config.WebsiteTitle,
Logo: config.Templates.LogoURI,
} }
subject := constants.MailWelcomeSubject subject := constants.MailWelcomeSubject
body, err := ParseTemplate("mail_welcome.html", data) body, err := ParseTemplate("mail_welcome.tmpl", data)
if err != nil { if err != nil {
logger.Error.Print("Couldn't send welcome mail") logger.Error.Print("Couldn't send welcome mail")
return err return err
@@ -130,6 +134,8 @@ func (s *EmailService) SendRegistrationNotification(user *models.User) error {
MembershipID int64 MembershipID int64
RentalFee float32 RentalFee float32
MembershipFee float32 MembershipFee float32
Logo string
WebsiteTitle string
}{ }{
Company: user.Company, Company: user.Company,
FirstName: user.FirstName, FirstName: user.FirstName,
@@ -146,10 +152,12 @@ func (s *EmailService) SendRegistrationNotification(user *models.User) error {
Phone: user.Phone, Phone: user.Phone,
IBAN: user.BankAccount.IBAN, IBAN: user.BankAccount.IBAN,
BASEURL: config.BaseURL, BASEURL: config.BaseURL,
Logo: config.Templates.LogoURI,
WebsiteTitle: config.WebsiteTitle,
} }
subject := constants.MailRegistrationSubject subject := constants.MailRegistrationSubject
body, err := ParseTemplate("mail_registration.html", data) body, err := ParseTemplate("mail_registration.tmpl", data)
if err != nil { if err != nil {
logger.Error.Print("Couldn't send admin notification mail") logger.Error.Print("Couldn't send admin notification mail")
return err return err
@@ -159,16 +167,20 @@ func (s *EmailService) SendRegistrationNotification(user *models.User) error {
func (s *EmailService) RelayContactFormMessage(sender string, name string, message string) error { func (s *EmailService) RelayContactFormMessage(sender string, name string, message string) error {
data := struct { data := struct {
Message string Message string
Name string Name string
BASEURL string BASEURL string
Logo string
WebsiteTitle string
}{ }{
Message: message, Message: message,
Name: name, Name: name,
BASEURL: config.BaseURL, BASEURL: config.BaseURL,
Logo: config.Templates.LogoURI,
WebsiteTitle: config.WebsiteTitle,
} }
subject := constants.MailContactSubject subject := constants.MailContactSubject
body, err := ParseTemplate("mail_contact_form.html", data) body, err := ParseTemplate("mail_contact_form.tmpl", data)
if err != nil { if err != nil {
logger.Error.Print("Couldn't send contact form message mail") logger.Error.Print("Couldn't send contact form message mail")
return err return err

View File

@@ -1,89 +0,0 @@
<!doctype html>
<html>
<body>
<div
style="
background-color: #f2f5f7;
color: #242424;
font-family: &quot;Helvetica Neue&quot;, &quot;Arial Nova&quot;,
&quot;Nimbus Sans&quot;, Arial, sans-serif;
font-size: 16px;
font-weight: 400;
letter-spacing: 0.15008px;
line-height: 1.5;
margin: 0;
padding: 32px 0;
min-height: 100%;
width: 100%;
"
>
<table
align="center"
width="100%"
style="margin: 0 auto; max-width: 600px; background-color: #ffffff"
role="presentation"
cellspacing="0"
cellpadding="0"
border="0"
>
<tbody>
<tr style="width: 100%">
<td>
<div style="padding: 0px 24px 0px 24px">
<a
href="https://carsharing-hasloh.de"
style="text-decoration: none"
target="_blank"
><img
alt="Carsharing-Hasloh"
src="https://carsharing-hasloh.de/images/CarsharingSH-Hasloh-LOGO.jpeg"
style="
outline: none;
border: none;
text-decoration: none;
vertical-align: middle;
display: inline-block;
max-width: 100%;
"
/></a>
</div>
<div style="font-weight: normal; padding: 0px 24px 16px 24px">
Moin Du Vorstand 👋,
</div>
<div style="font-weight: normal; padding: 0px 24px 16px 24px">
<p>Eine neue Kontaktanfrage<br />{{.Name}} hat geschrieben</p>
</div>
<div
style="
font-size: 22px;
font-weight: normal;
text-align: center;
padding: 0px 24px 0px 24px;
"
>
Hier ist die Nachricht:
</div>
<div style="font-weight: normal; padding: 16px 24px 0px 24px">
<p>{{.Message}}</p>
</div>
<div style="padding: 16px 0px 16px 0px">
<hr
style="
width: 100%;
border: none;
border-top: 1px solid #cccccc;
margin: 0;
"
/>
</div>
<div style="font-weight: normal; padding: 16px 24px 16px 24px">
<p>Mit freundlichen Grüßen,</p>
<p>Dein untertänigster Wolkenrechner</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -0,0 +1,127 @@
Moin Du Vorstand 👋,
Eine neue Kontaktanfrage!
{{.Name}} hat geschrieben
Hier ist die Nachricht:
{{.Message}}
Mit freundlichen Grüßen,
Dein untertänigster Wolkenrechner
<!doctype html>
<html>
<body>
<div
style="
background-color: #f2f5f7;
color: #242424;
font-family: &quot;Helvetica Neue&quot;, &quot;Arial Nova&quot;,
&quot;Nimbus Sans&quot;, Arial, sans-serif;
font-size: 16px;
font-weight: 400;
letter-spacing: 0.15008px;
line-height: 1.5;
margin: 0;
padding: 32px 0;
min-height: 100%;
width: 100%;
"
>
<table
align="center"
width="100%"
style="
margin: 0 auto;
max-width: 600px;
background-color: #ffffff;
"
role="presentation"
cellspacing="0"
cellpadding="0"
border="0"
>
<tbody>
<tr style="width: 100%">
<td>
<div style="padding: 0px 24px 0px 24px">
<a
href="{{.BASEURL}}"
style="text-decoration: none"
target="_blank"
><img
alt="{{.WebsiteTitle}}"
src="{{.BASEURL}}{{.Logo}}"
style="
outline: none;
border: none;
text-decoration: none;
vertical-align: middle;
display: inline-block;
max-width: 100%;
"
/></a>
</div>
<div
style="
font-weight: normal;
padding: 0px 24px 16px 24px;
"
>
Moin Du Vorstand 👋,
</div>
<div
style="
font-weight: normal;
padding: 0px 24px 16px 24px;
"
>
<p>
Eine neue Kontaktanfrage<br />{{.Name}} hat
geschrieben
</p>
</div>
<div
style="
font-size: 22px;
font-weight: normal;
text-align: center;
padding: 0px 24px 0px 24px;
"
>
Hier ist die Nachricht:
</div>
<div
style="
font-weight: normal;
padding: 16px 24px 0px 24px;
"
>
<p>{{.Message}}</p>
</div>
<div style="padding: 16px 0px 16px 0px">
<hr
style="
width: 100%;
border: none;
border-top: 1px solid #cccccc;
margin: 0;
"
/>
</div>
<div
style="
font-weight: normal;
padding: 16px 24px 16px 24px;
"
>
<p>Mit freundlichen Grüßen,</p>
<p>Dein untertänigster Wolkenrechner</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

View File

@@ -1,3 +1,32 @@
Moin Du Vorstand 👋,
Ein neues Mitglied!!!<br />{{.FirstName}} {{.LastName}} hat sich registriert.
Hier sind die Daten:
---------------------
Das gebuchtes Modell:
Name: {{.MembershipModel}}
Preis/Monat: {{.MembershipFee}}
Preis/h: {{.RentalFee}}
Persönliche Daten:
{{if .Company}}
Firma: {{.Company}}
{{end}}
Name: {{.FirstName}} {{.LastName}}
Mitgliedsnr: {{.MembershipID}}
Adresse: {{.Address}},
{{.ZipCode}} {{.City}}
Geburtsdatum: {{.DateOfBirth}}
Email: {{.Email}}
Telefon: {{.Phone}}
IBAN: {{.IBAN}}
Mit freundlichen Grüßen,
Dein untertänigster Wolkenrechner
<!doctype html> <!doctype html>
<html> <html>
<body> <body>
@@ -35,8 +64,8 @@
style="text-decoration: none" style="text-decoration: none"
target="_blank" target="_blank"
><img ><img
alt="Carsharing-Hasloh" alt="{{.WebsiteTitle}}"
src="{{.BASEURL}}/images/CarsharingSH-Hasloh-LOGO.jpeg" src="{{.BASEURL}}{{.Logo}}"
style=" style="
outline: none; outline: none;
border: none; border: none;

View File

@@ -1,3 +1,27 @@
Moin {{.FirstName}} {{.LastName}} 👋,
herzlich willkommen beim Dörpsmobil Hasloh e.V.! Vielen Dank für
Ihre Registrierung und Ihre Unterstützung unseres Projekts.
Um die Registrierung abschließen zu können bestätigen Sie bitte
noch Ihre Emailadresse indem Sie hier klicken:
E-Mail Adresse bestätigen
{{.BASEURL}}/backend/verify?token={{.Token}}
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir
Ihnen alle weiteren Informationen zu. Wir freuen uns auf die
gemeinsame Zeit mit Ihnen!
Sollte es Probleme geben, möchten wir uns gerne jetzt schon
dafür entschuldigen, wenden Sie sich gerne an uns, wir werden
uns sofort darum kümmern, versprochen! Antworten Sie einfach auf
diese E-Mail.
Mit Freundlichen Grüßen,
Der Vorstand
<!doctype html> <!doctype html>
<html> <html>
<body> <body>

View File

@@ -1,3 +1,59 @@
Moin {{.FirstName}} {{if .Company}}({{.Company}}){{end}}👋,
wir freuen uns sehr, dich als neues Mitglied bei Carsharing
Hasloh begrüßen zu dürfen! Herzlichen Glückwunsch zur
erfolgreichen E-Mail-Verifikation und willkommen in unserem
Verein!
Hier einige wichtige Informationen für dich:
Deine Mitgliedsnummer: {{.MembershipID}}
Dein gebuchtes Modell:
Name: {{.MembershipModel}}
Preis/Monat: {{.MembershipFee}}
Preis/h: {{.RentalFee}}
Mitgliedsbeitrag: Solange wir noch kein
Fahrzeug im Betrieb haben, zahlst Du sinnvollerweise auch
keinen Mitgliedsbeitrag. Es ist zur Zeit der 1.1.2025 als
Startdatum geplant.
Führerscheinverifikation: Weitere Informationen zur Verifikation
deines Führerscheins folgen in Kürze. Du musst nichts weiter tun,
wir werden uns bei dir melden, sobald es notwendig ist.
Moqo App:
Wir werden die Moqo App nutzen,
um das Fahrzeug ausleihen zu können. Wenn Du schon mal einen
ersten Eindruck von dem Buchungsvorgang haben möchtest,
schaue Dir gerne dieses kurze Video an:
Moqo App Nutzung
https://www.youtube.com/shorts/ZMKUX0uyOps
Dörpsmobil:
Wir sind nicht alleine sondern Mitglied in einem Schleswig-Holstein
weiten Netz an gemeinnützigen Carsharing Anbietern. Für mehr
Informationen zu diesem Netzwerk haben wir auch ein Video vorbereitet:
Dörpsmobil SH
https://www.youtube.com/watch?v=NSch-2F-ru0
Für mehr Informationen besuche gerne unsere Webseite:
Carsharing-Hasloh.de
{{.BASEURL}}
Solltest du Fragen haben oder Unterstützung benötigen, kannst
du dich jederzeit an unsere Vorsitzende wenden:
Anke Freitag
E-Mail: vorstand@carsharing-hasloh.de
Telefon: +49 176 5013 4256
Wir danken dir herzlich für dein Vertrauen in uns und freuen uns
darauf, dich hoffentlich bald mit einem Auto begrüßen zu dürfen.
Mit freundlichen Grüßen,
Dein Carsharing Hasloh Team
<!doctype html> <!doctype html>
<html> <html>
<body> <body>
@@ -35,8 +91,8 @@
style="text-decoration: none" style="text-decoration: none"
target="_blank" target="_blank"
><img ><img
alt="Carsharing Hasloh" alt="{{.WebsiteTitle}}"
src="{{.BASEURL}}/images/CarsharingSH-Hasloh-LOGO.jpeg" src="{{.BASEURL}}{{.Logo}}"
style=" style="
outline: none; outline: none;
border: none; border: none;