feat: Notification translation (#192)

* feat: translate emails

* feat: translate emails

* fix: add notification statik to build

* fix: add codes to templates
This commit is contained in:
Fabi
2020-06-09 15:11:42 +02:00
committed by GitHub
parent e87fca28e7
commit 17f0eea4a1
18 changed files with 186 additions and 57 deletions

View File

@@ -5,6 +5,9 @@ import (
"github.com/caos/logging"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing"
"github.com/rakyll/statik/fs"
_ "github.com/caos/zitadel/internal/notification/statik"
)
type Config struct {
@@ -12,6 +15,9 @@ type Config struct {
}
func Start(ctx context.Context, config Config, systemDefaults sd.SystemDefaults) {
_, err := eventsourcing.Start(config.Repository, systemDefaults)
statikFS, err := fs.NewWithNamespace("notification")
logging.Log("CONFI-7usEW").OnError(err).Panic("unable to start listener")
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults)
logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/view"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
"time"
@@ -29,14 +30,21 @@ type EventstoreRepos struct {
UserEvents *usr_event.UserEventstore
}
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []spooler.Handler {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator) []spooler.Handler {
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
if err != nil {
logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto")
}
return []spooler.Handler{
&NotifyUser{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}},
&Notification{handler: handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount}, eventstore: eventstore, userEvents: repos.UserEvents, systemDefaults: systemDefaults, AesCrypto: aesCrypto},
&Notification{
handler: handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount},
eventstore: eventstore,
userEvents: repos.UserEvents,
systemDefaults: systemDefaults,
AesCrypto: aesCrypto,
i18n: i18n,
},
}
}

View File

@@ -6,6 +6,7 @@ import (
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/types"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@@ -24,6 +25,7 @@ type Notification struct {
userEvents *usr_event.UserEventstore
systemDefaults sd.SystemDefaults
AesCrypto crypto.EncryptionAlgorithm
i18n *i18n.Translator
}
const (
@@ -75,7 +77,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
if err != nil {
return err
}
err = types.SendUserInitCode(user, initCode, n.systemDefaults, n.AesCrypto)
err = types.SendUserInitCode(n.i18n, user, initCode, n.systemDefaults, n.AesCrypto)
if err != nil {
return err
}
@@ -93,7 +95,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil {
return err
}
err = types.SendPasswordCode(user, pwCode, n.systemDefaults, n.AesCrypto)
err = types.SendPasswordCode(n.i18n, user, pwCode, n.systemDefaults, n.AesCrypto)
if err != nil {
return err
}
@@ -111,7 +113,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
if err != nil {
return err
}
err = types.SendEmailVerificationCode(user, emailCode, n.systemDefaults, n.AesCrypto)
err = types.SendEmailVerificationCode(n.i18n, user, emailCode, n.systemDefaults, n.AesCrypto)
if err != nil {
return err
}
@@ -129,7 +131,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil {
return err
}
err = types.SendPhoneVerificationCode(user, phoneCode, n.systemDefaults, n.AesCrypto)
err = types.SendPhoneVerificationCode(n.i18n, user, phoneCode, n.systemDefaults, n.AesCrypto)
if err != nil {
return err
}

View File

@@ -5,23 +5,27 @@ import (
"github.com/caos/zitadel/internal/config/types"
es_int "github.com/caos/zitadel/internal/eventstore"
es_spol "github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/handler"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/spooler"
noti_view "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view"
es_usr "github.com/caos/zitadel/internal/user/repository/eventsourcing"
"golang.org/x/text/language"
"net/http"
)
type Config struct {
Eventstore es_int.Config
View types.SQL
Spooler spooler.SpoolerConfig
DefaultLanguage language.Tag
Eventstore es_int.Config
View types.SQL
Spooler spooler.SpoolerConfig
}
type EsRepository struct {
spooler *es_spol.Spooler
}
func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
es, err := es_int.Start(conf.Eventstore)
if err != nil {
return nil, err
@@ -43,8 +47,12 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error)
if err != nil {
return nil, err
}
i18n, err := i18n.NewTranslator(dir, i18n.TranslatorConfig{DefaultLanguage: conf.DefaultLanguage})
if err != nil {
return nil, err
}
eventstoreRepos := handler.EventstoreRepos{UserEvents: user}
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos, systemDefaults)
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos, systemDefaults, i18n)
return &EsRepository{
spool,

View File

@@ -5,6 +5,7 @@ import (
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/handler"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/view"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@@ -21,12 +22,12 @@ type EventstoreRepos struct {
UserEvents *usr_event.UserEventstore
}
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults) *spooler.Spooler {
func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator) *spooler.Spooler {
spoolerConfig := spooler.Config{
Eventstore: es,
Locker: &locker{dbClient: sql},
ConcurrentTasks: c.ConcurrentTasks,
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, eventstoreRepos, systemDefaults),
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, eventstoreRepos, systemDefaults, i18n),
}
spool := spoolerConfig.New()
spool.Start()

View File

@@ -0,0 +1,29 @@
InitCode:
Title: Zitadel - User initialisieren
PreHeader: User initialisieren
Subject: User initialisieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Du kannst den Button unten verwenden, um die Initialisierung abzuschliesen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.
ButtonText: Initialisierung abschliessen
PasswordReset:
Title: Zitadel - Passwort zurücksetzen
PreHeader: Passwort zurücksetzen
Subject: Passwort zurücksetzen
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren.
ButtonText: Passwort zurücksetzen
VerifyEmail:
Title: Zitadel - Email verifizieren
PreHeader: Email verifizieren
Subject: Email verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren. (Code {{.Code}}) Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren.
ButtonText: Email verifizieren
VerifyPhone:
Title: Zitadel - Telefonnummer verifizieren
PreHeader: Telefonnummer verifizieren
Subject: Telefonnummer verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst {{.Code}}
ButtonText: Telefon verifizieren

View File

@@ -0,0 +1,29 @@
InitCode:
Title: Zitadel - Initialize User
PreHeader: Initialize User
Subject: Initialize User
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: This user was created in Zitadel. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it.
ButtonText: Finish initialization
PasswordReset:
Title: Zitadel - Reset password
PreHeader: Reset password
Subject: Reset password
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: We received a password reset request. Please use the button below to reset your password. (Code {{.Code}}) If you didn't ask for this mail, please ignore it.
ButtonText: Reset password
VerifyEmail:
Title: Zitadel - Verify email
PreHeader: Verify email
Subject: Verify email
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you din't add a new email, please ignore this email.
ButtonText: Verify email
VerifyPhone:
Title: Zitadel - Verify phone
PreHeader: Verify phone
Subject: Verify phone
Greeting: Hello {{.FirstName}} {{.LastName}},
Text: A new phonenumber has been added. Please use the following code to verify it {{.Code}}
ButtonText: Verify phone

View File

@@ -0,0 +1,3 @@
package statik
//go:generate statik -src=../static -dest=.. -ns=notification

View File

@@ -1,5 +1,9 @@
package templates
import (
"github.com/caos/zitadel/internal/i18n"
)
type TemplateData struct {
Title string
PreHeader string
@@ -9,3 +13,13 @@ type TemplateData struct {
Href string
ButtonText string
}
func (data *TemplateData) Translate(i18n *i18n.Translator, args map[string]interface{}, langs ...string) {
data.Title = i18n.Localize(data.Title, nil, langs...)
data.PreHeader = i18n.Localize(data.PreHeader, nil, langs...)
data.Subject = i18n.Localize(data.Subject, nil, langs...)
data.Greeting = i18n.Localize(data.Greeting, args, langs...)
data.Text = i18n.Localize(data.Text, args, langs...)
data.Href = i18n.Localize(data.Href, nil, langs...)
data.ButtonText = i18n.Localize(data.ButtonText, nil, langs...)
}

View File

@@ -3,6 +3,7 @@ package types
import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@@ -10,12 +11,10 @@ import (
type EmailVerificationCodeData struct {
templates.TemplateData
FirstName string
LastName string
URL string
URL string
}
func SendEmailVerificationCode(user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendEmailVerificationCode(i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -24,7 +23,13 @@ func SendEmailVerificationCode(user *view_model.NotifyUser, code *es_model.Email
if err != nil {
return err
}
emailCodeData := &EmailVerificationCodeData{TemplateData: systemDefaults.Notifications.TemplateData.VerifyEmail, FirstName: user.FirstName, LastName: user.LastName, URL: url}
var args = map[string]interface{}{
"FirstName": user.FirstName,
"LastName": user.LastName,
"Code": codeString,
}
systemDefaults.Notifications.TemplateData.VerifyEmail.Translate(i18n, args, user.PreferredLanguage)
emailCodeData := &EmailVerificationCodeData{TemplateData: systemDefaults.Notifications.TemplateData.VerifyEmail, URL: url}
template, err := templates.GetParsedTemplate(emailCodeData)
if err != nil {

View File

@@ -3,6 +3,7 @@ package types
import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@@ -10,9 +11,7 @@ import (
type InitCodeEmailData struct {
templates.TemplateData
FirstName string
LastName string
URL string
URL string
}
type UrlData struct {
@@ -20,7 +19,7 @@ type UrlData struct {
Code string
}
func SendUserInitCode(user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendUserInitCode(i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -29,7 +28,13 @@ func SendUserInitCode(user *view_model.NotifyUser, code *es_model.InitUserCode,
if err != nil {
return err
}
initCodeData := &InitCodeEmailData{TemplateData: systemDefaults.Notifications.TemplateData.InitCode, FirstName: user.FirstName, LastName: user.LastName, URL: url}
var args = map[string]interface{}{
"FirstName": user.FirstName,
"LastName": user.LastName,
"Code": codeString,
}
systemDefaults.Notifications.TemplateData.InitCode.Translate(i18n, args, user.PreferredLanguage)
initCodeData := &InitCodeEmailData{TemplateData: systemDefaults.Notifications.TemplateData.InitCode, URL: url}
template, err := templates.GetParsedTemplate(initCodeData)
if err != nil {

View File

@@ -3,6 +3,7 @@ package types
import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@@ -15,7 +16,7 @@ type PasswordCodeData struct {
URL string
}
func SendPasswordCode(user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendPasswordCode(i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -24,6 +25,12 @@ func SendPasswordCode(user *view_model.NotifyUser, code *es_model.PasswordCode,
if err != nil {
return err
}
var args = map[string]interface{}{
"FirstName": user.FirstName,
"LastName": user.LastName,
"Code": codeString,
}
systemDefaults.Notifications.TemplateData.PasswordReset.Translate(i18n, args, user.PreferredLanguage)
passwordCodeData := &PasswordCodeData{TemplateData: systemDefaults.Notifications.TemplateData.PasswordReset, FirstName: user.FirstName, LastName: user.LastName, URL: url}
template, err := templates.GetParsedTemplate(passwordCodeData)

View File

@@ -3,24 +3,28 @@ package types
import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
type PhoneVerificationCodeData struct {
FirstName string
LastName string
Code string
UserID string
UserID string
}
func SendPhoneVerificationCode(user *view_model.NotifyUser, code *es_model.PhoneCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendPhoneVerificationCode(i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.PhoneCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
}
codeData := &PhoneVerificationCodeData{FirstName: user.FirstName, LastName: user.LastName, UserID: user.ID, Code: codeString}
var args = map[string]interface{}{
"FirstName": user.FirstName,
"LastName": user.LastName,
"Code": codeString,
}
systemDefaults.Notifications.TemplateData.VerifyPhone.Translate(i18n, args, user.PreferredLanguage)
codeData := &PhoneVerificationCodeData{UserID: user.ID}
template, err := templates.ParseTemplateText(systemDefaults.Notifications.TemplateData.VerifyPhone.Text, codeData)
if err != nil {
return err