Extracting email functions into a service (#14802)

* Extracting email functions into a service

* Fixing two shadowing errors

* Address PR review comments

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Jesús Espino
2020-07-07 10:03:21 +02:00
коммит произвёл GitHub
родитель 10f5a8890c
Коммит b977017ca7
19 изменённых файлов: 232 добавлений и 391 удалений

Просмотреть файл

@@ -84,7 +84,7 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
} }
auditRec.AddMeta("errors", errList) auditRec.AddMeta("errors", errList)
if len(goodEmails) > 0 { if len(goodEmails) > 0 {
c.App.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL) c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL)
} }
// in graceful mode we return both the successful ones and the failed ones // in graceful mode we return both the successful ones and the failed ones
w.Write([]byte(model.EmailInviteWithErrorToJson(invitesWithErrors))) w.Write([]byte(model.EmailInviteWithErrorToJson(invitesWithErrors)))
@@ -101,7 +101,7 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
c.Err = model.NewAppError("localInviteUsersToTeam", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest) c.Err = model.NewAppError("localInviteUsersToTeam", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest)
return return
} }
c.App.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), emailList, *c.App.Config().ServiceSettings.SiteURL) c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), emailList, *c.App.Config().ServiceSettings.SiteURL)
ReturnStatusOK(w) ReturnStatusOK(w)
} }
auditRec.Success() auditRec.Success()

Просмотреть файл

@@ -1185,7 +1185,7 @@ func updateUserActive(c *Context, w http.ResponseWriter, r *http.Request) {
if isSelfDeactive { if isSelfDeactive {
c.App.Srv().Go(func() { c.App.Srv().Go(func() {
if err = c.App.SendDeactivateAccountEmail(user.Email, user.Locale, c.App.GetSiteURL()); err != nil { if err = c.App.Srv().EmailService.SendDeactivateAccountEmail(user.Email, user.Locale, c.App.GetSiteURL()); err != nil {
mlog.Error(err.Error()) mlog.Error(err.Error())
} }
}) })

Просмотреть файл

@@ -3068,7 +3068,7 @@ func TestVerifyUserEmail(t *testing.T) {
ruser, _ := th.Client.CreateUser(&user) ruser, _ := th.Client.CreateUser(&user)
token, err := th.App.CreateVerifyEmailToken(ruser.Id, email) token, err := th.App.Srv().EmailService.CreateVerifyEmailToken(ruser.Id, email)
require.Nil(t, err, "Unable to create email verify token") require.Nil(t, err, "Unable to create email verify token")
_, resp := th.Client.VerifyUserEmail(token.Token) _, resp := th.Client.VerifyUserEmail(token.Token)

Просмотреть файл

@@ -334,7 +334,6 @@ type AppIface interface {
AddChannelMember(userId string, channel *model.Channel, userRequestorId string, postRootId string) (*model.ChannelMember, *model.AppError) AddChannelMember(userId string, channel *model.Channel, userRequestorId string, postRootId string) (*model.ChannelMember, *model.AppError)
AddConfigListener(listener func(*model.Config, *model.Config)) string AddConfigListener(listener func(*model.Config, *model.Config)) string
AddDirectChannels(teamId string, user *model.User) *model.AppError AddDirectChannels(teamId string, user *model.User) *model.AppError
AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError
AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppError AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppError
AddSamlPrivateCertificate(fileData *multipart.FileHeader) *model.AppError AddSamlPrivateCertificate(fileData *multipart.FileHeader) *model.AppError
AddSamlPublicCertificate(fileData *multipart.FileHeader) *model.AppError AddSamlPublicCertificate(fileData *multipart.FileHeader) *model.AppError
@@ -424,7 +423,6 @@ type AppIface interface {
CreateUserFromSignup(user *model.User) (*model.User, *model.AppError) CreateUserFromSignup(user *model.User) (*model.User, *model.AppError)
CreateUserWithInviteId(user *model.User, inviteId string) (*model.User, *model.AppError) CreateUserWithInviteId(user *model.User, inviteId string) (*model.User, *model.AppError)
CreateUserWithToken(user *model.User, token *model.Token) (*model.User, *model.AppError) CreateUserWithToken(user *model.User, token *model.Token) (*model.User, *model.AppError)
CreateVerifyEmailToken(userId string, newEmail string) (*model.Token, *model.AppError)
CreateWebhookPost(userId string, channel *model.Channel, text, overrideUsername, overrideIconUrl, overrideIconEmoji string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) CreateWebhookPost(userId string, channel *model.Channel, text, overrideUsername, overrideIconUrl, overrideIconEmoji string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError)
DataRetention() einterfaces.DataRetentionInterface DataRetention() einterfaces.DataRetentionInterface
DeactivateGuests() *model.AppError DeactivateGuests() *model.AppError
@@ -852,15 +850,10 @@ type AppIface interface {
SendAckToPushProxy(ack *model.PushNotificationAck) error SendAckToPushProxy(ack *model.PushNotificationAck) error
SendAutoResponse(channel *model.Channel, receiver *model.User) (bool, *model.AppError) SendAutoResponse(channel *model.Channel, receiver *model.User) (bool, *model.AppError)
SendAutoResponseIfNecessary(channel *model.Channel, sender *model.User) (bool, *model.AppError) SendAutoResponseIfNecessary(channel *model.Channel, sender *model.User) (bool, *model.AppError)
SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError
SendEmailVerification(user *model.User, newEmail string) *model.AppError SendEmailVerification(user *model.User, newEmail string) *model.AppError
SendEphemeralPost(userId string, post *model.Post) *model.Post SendEphemeralPost(userId string, post *model.Post) *model.Post
SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string)
SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList, setOnline bool) ([]string, error) SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList, setOnline bool) ([]string, error)
SendPasswordReset(email string, siteURL string) (bool, *model.AppError) SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError)
SendRemoveExpiredLicenseEmail(email string, locale, siteURL string, licenseId string) *model.AppError
SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
ServePluginRequest(w http.ResponseWriter, r *http.Request) ServePluginRequest(w http.ResponseWriter, r *http.Request)
Session() *model.Session Session() *model.Session

Просмотреть файл

@@ -5,6 +5,7 @@ package app
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
@@ -39,7 +40,22 @@ func condenseSiteURL(siteURL string) string {
return path.Join(parsedSiteURL.Host, parsedSiteURL.Path) return path.Join(parsedSiteURL.Host, parsedSiteURL.Path)
} }
func (s *Server) setupInviteEmailRateLimiting() error { type EmailService struct {
srv *Server
EmailRateLimiter *throttled.GCRARateLimiter
EmailBatching *EmailBatchingJob
}
func NewEmailService(srv *Server) (*EmailService, error) {
service := &EmailService{srv: srv}
if err := service.setupInviteEmailRateLimiting(); err != nil {
return nil, err
}
service.InitEmailBatching()
return service, nil
}
func (es *EmailService) setupInviteEmailRateLimiting() error {
store, err := memstore.New(emailRateLimitingMemstoreSize) store, err := memstore.New(emailRateLimitingMemstoreSize)
if err != nil { if err != nil {
return errors.Wrap(err, "Unable to setup email rate limiting memstore.") return errors.Wrap(err, "Unable to setup email rate limiting memstore.")
@@ -55,77 +71,77 @@ func (s *Server) setupInviteEmailRateLimiting() error {
return errors.Wrap(err, "Unable to setup email rate limiting GCRA rate limiter.") return errors.Wrap(err, "Unable to setup email rate limiting GCRA rate limiter.")
} }
s.EmailRateLimiter = rateLimiter es.EmailRateLimiter = rateLimiter
return nil return nil
} }
func (a *App) sendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL string) *model.AppError { func (es *EmailService) sendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.username_change_subject", subject := T("api.templates.username_change_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"TeamDisplayName": a.Config().TeamSettings.SiteName}) "TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("email_change_body", locale) bodyPage := es.newEmailTemplate("email_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.username_change_body.title") bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
bodyPage.Props["Info"] = T("api.templates.username_change_body.info", bodyPage.Props["Info"] = T("api.templates.username_change_body.info",
map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "NewUsername": newUsername}) map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "NewUsername": newUsername})
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendEmailChangeVerifyEmail(newUserEmail, locale, siteURL, token string) *model.AppError { func (es *EmailService) sendEmailChangeVerifyEmail(newUserEmail, locale, siteURL, token string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(newUserEmail)) link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(newUserEmail))
subject := T("api.templates.email_change_verify_subject", subject := T("api.templates.email_change_verify_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"TeamDisplayName": a.Config().TeamSettings.SiteName}) "TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("email_change_verify_body", locale) bodyPage := es.newEmailTemplate("email_change_verify_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title") bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title")
bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info", bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info",
map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName}) map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
bodyPage.Props["VerifyUrl"] = link bodyPage.Props["VerifyUrl"] = link
bodyPage.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button") bodyPage.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button")
if err := a.sendMail(newUserEmail, subject, bodyPage.Render()); err != nil { if err := es.sendMail(newUserEmail, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.AppError { func (es *EmailService) sendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.email_change_subject", subject := T("api.templates.email_change_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"TeamDisplayName": a.Config().TeamSettings.SiteName}) "TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("email_change_body", locale) bodyPage := es.newEmailTemplate("email_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.email_change_body.title") bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
bodyPage.Props["Info"] = T("api.templates.email_change_body.info", bodyPage.Props["Info"] = T("api.templates.email_change_body.info",
map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "NewEmail": newEmail}) map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "NewEmail": newEmail})
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(oldEmail, subject, bodyPage.Render()); err != nil { if err := es.sendMail(oldEmail, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendVerifyEmail(userEmail, locale, siteURL, token string) *model.AppError { func (es *EmailService) sendVerifyEmail(userEmail, locale, siteURL, token string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(userEmail)) link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(userEmail))
@@ -133,44 +149,44 @@ func (a *App) sendVerifyEmail(userEmail, locale, siteURL, token string) *model.A
serverURL := condenseSiteURL(siteURL) serverURL := condenseSiteURL(siteURL)
subject := T("api.templates.verify_subject", subject := T("api.templates.verify_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("verify_body", locale) bodyPage := es.newEmailTemplate("verify_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": serverURL}) bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": serverURL})
bodyPage.Props["Info"] = T("api.templates.verify_body.info") bodyPage.Props["Info"] = T("api.templates.verify_body.info")
bodyPage.Props["VerifyUrl"] = link bodyPage.Props["VerifyUrl"] = link
bodyPage.Props["Button"] = T("api.templates.verify_body.button") bodyPage.Props["Button"] = T("api.templates.verify_body.button")
if err := a.sendMail(userEmail, subject, bodyPage.Render()); err != nil { if err := es.sendMail(userEmail, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError { func (es *EmailService) SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.signin_change_email.subject", subject := T("api.templates.signin_change_email.subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("signin_change_body", locale) bodyPage := es.newEmailTemplate("signin_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title") bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
bodyPage.Props["Info"] = T("api.templates.signin_change_email.body.info", bodyPage.Props["Info"] = T("api.templates.signin_change_email.body.info",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], "Method": method}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName, "Method": method})
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError { func (es *EmailService) sendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError {
if !*a.Config().EmailSettings.SendEmailNotifications && !*a.Config().EmailSettings.RequireEmailVerification { if !*es.srv.Config().EmailSettings.SendEmailNotifications && !*es.srv.Config().EmailSettings.RequireEmailVerification {
return model.NewAppError("SendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, "Send Email Notifications and Require Email Verification is disabled in the system console", http.StatusInternalServerError) return model.NewAppError("SendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, "Send Email Notifications and Require Email Verification is disabled in the system console", http.StatusInternalServerError)
} }
@@ -179,10 +195,10 @@ func (a *App) sendWelcomeEmail(userId string, email string, verified bool, local
serverURL := condenseSiteURL(siteURL) serverURL := condenseSiteURL(siteURL)
subject := T("api.templates.welcome_subject", subject := T("api.templates.welcome_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"ServerURL": serverURL}) "ServerURL": serverURL})
bodyPage := a.newEmailTemplate("welcome_body", locale) bodyPage := es.newEmailTemplate("welcome_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": serverURL}) bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": serverURL})
bodyPage.Props["Info"] = T("api.templates.welcome_body.info") bodyPage.Props["Info"] = T("api.templates.welcome_body.info")
@@ -191,13 +207,13 @@ func (a *App) sendWelcomeEmail(userId string, email string, verified bool, local
bodyPage.Props["Info3"] = T("api.templates.welcome_body.info3") bodyPage.Props["Info3"] = T("api.templates.welcome_body.info3")
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
if *a.Config().NativeAppSettings.AppDownloadLink != "" { if *es.srv.Config().NativeAppSettings.AppDownloadLink != "" {
bodyPage.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info") bodyPage.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info")
bodyPage.Props["AppDownloadLink"] = *a.Config().NativeAppSettings.AppDownloadLink bodyPage.Props["AppDownloadLink"] = *es.srv.Config().NativeAppSettings.AppDownloadLink
} }
if !verified && *a.Config().EmailSettings.RequireEmailVerification { if !verified && *es.srv.Config().EmailSettings.RequireEmailVerification {
token, err := a.CreateVerifyEmailToken(userId, email) token, err := es.CreateVerifyEmailToken(userId, email)
if err != nil { if err != nil {
return err return err
} }
@@ -205,63 +221,63 @@ func (a *App) sendWelcomeEmail(userId string, email string, verified bool, local
bodyPage.Props["VerifyUrl"] = link bodyPage.Props["VerifyUrl"] = link
} }
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppError { func (es *EmailService) sendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.password_change_subject", subject := T("api.templates.password_change_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"TeamDisplayName": a.Config().TeamSettings.SiteName}) "TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("password_change_body", locale) bodyPage := es.newEmailTemplate("password_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.password_change_body.title") bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
bodyPage.Props["Info"] = T("api.templates.password_change_body.info", bodyPage.Props["Info"] = T("api.templates.password_change_body.info",
map[string]interface{}{"TeamDisplayName": a.Config().TeamSettings.SiteName, "TeamURL": siteURL, "Method": method}) map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "TeamURL": siteURL, "Method": method})
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) sendUserAccessTokenAddedEmail(email, locale, siteURL string) *model.AppError { func (es *EmailService) sendUserAccessTokenAddedEmail(email, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.user_access_token_subject", subject := T("api.templates.user_access_token_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("password_change_body", locale) bodyPage := es.newEmailTemplate("password_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title") bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title")
bodyPage.Props["Info"] = T("api.templates.user_access_token_body.info", bodyPage.Props["Info"] = T("api.templates.user_access_token_body.info",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], "SiteURL": siteURL}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName, "SiteURL": siteURL})
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("sendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("sendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError) { func (es *EmailService) SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError) {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
link := fmt.Sprintf("%s/reset_password_complete?token=%s", siteURL, url.QueryEscape(token.Token)) link := fmt.Sprintf("%s/reset_password_complete?token=%s", siteURL, url.QueryEscape(token.Token))
subject := T("api.templates.reset_subject", subject := T("api.templates.reset_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("reset_body", locale) bodyPage := es.newEmailTemplate("reset_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.reset_body.title") bodyPage.Props["Title"] = T("api.templates.reset_body.title")
bodyPage.Props["Info1"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info1", nil) bodyPage.Props["Info1"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info1", nil)
@@ -269,20 +285,20 @@ func (a *App) SendPasswordResetEmail(email string, token *model.Token, locale, s
bodyPage.Props["ResetUrl"] = link bodyPage.Props["ResetUrl"] = link
bodyPage.Props["Button"] = T("api.templates.reset_body.button") bodyPage.Props["Button"] = T("api.templates.reset_body.button")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError) return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError)
} }
return true, nil return true, nil
} }
func (a *App) sendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError { func (es *EmailService) sendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.mfa_change_subject", subject := T("api.templates.mfa_change_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("mfa_change_body", locale) bodyPage := es.newEmailTemplate("mfa_change_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
if activated { if activated {
@@ -294,26 +310,26 @@ func (a *App) sendMfaChangeEmail(email string, activated bool, locale, siteURL s
} }
bodyPage.Props["Warning"] = T("api.templates.email_warning") bodyPage.Props["Warning"] = T("api.templates.email_warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) { func (es *EmailService) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) {
if a.Srv().EmailRateLimiter == nil { if es.EmailRateLimiter == nil {
a.Log().Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id)) es.srv.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id))
return return
} }
rateLimited, result, err := a.Srv().EmailRateLimiter.RateLimit(senderUserId, len(invites)) rateLimited, result, err := es.EmailRateLimiter.RateLimit(senderUserId, len(invites))
if err != nil { if err != nil {
a.Log().Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err)) es.srv.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err))
return return
} }
if rateLimited { if rateLimited {
a.Log().Error("Invite emails rate limited.", es.srv.Log.Error("Invite emails rate limited.",
mlog.String("user_id", senderUserId), mlog.String("user_id", senderUserId),
mlog.String("team_id", team.Id), mlog.String("team_id", team.Id),
mlog.String("retry_after", result.RetryAfter.String()), mlog.String("retry_after", result.RetryAfter.String()),
@@ -326,9 +342,9 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId
subject := utils.T("api.templates.invite_subject", subject := utils.T("api.templates.invite_subject",
map[string]interface{}{"SenderName": senderName, map[string]interface{}{"SenderName": senderName,
"TeamDisplayName": team.DisplayName, "TeamDisplayName": team.DisplayName,
"SiteName": a.ClientConfig()["SiteName"]}) "SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("invite_body", model.DEFAULT_LOCALE) bodyPage := es.newEmailTemplate("invite_body", model.DEFAULT_LOCALE)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title") bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info", bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info",
@@ -349,43 +365,32 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId
props["name"] = team.Name props["name"] = team.Name
data := model.MapToJson(props) data := model.MapToJson(props)
if err := a.Srv().Store.Token().Save(token); err != nil { if err := es.srv.Store.Token().Save(token); err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue continue
} }
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
if err := a.sendMail(invite, subject, bodyPage.Render()); err != nil { if err := es.sendMail(invite, subject, bodyPage.Render()); err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
} }
} }
} }
} }
func (a *App) sendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, invites []string, siteURL string, message string) { func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string) {
if a.Srv().EmailRateLimiter == nil { if es.EmailRateLimiter == nil {
a.Log().Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id)) es.srv.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id))
return return
} }
rateLimited, result, err := a.Srv().EmailRateLimiter.RateLimit(senderUserId, len(invites)) rateLimited, result, err := es.EmailRateLimiter.RateLimit(senderUserId, len(invites))
if err != nil { if err != nil {
a.Log().Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err)) es.srv.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err))
return return
} }
sender, appErr := a.GetUser(senderUserId)
if appErr != nil {
a.Log().Error("Email invite not sent, unable to find the sender user.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(appErr))
return
}
senderProfileImage, _, appErr := a.GetProfileImage(sender)
if appErr != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(appErr))
}
if rateLimited { if rateLimited {
a.Log().Error("Invite emails rate limited.", es.srv.Log.Error("Invite emails rate limited.",
mlog.String("user_id", senderUserId), mlog.String("user_id", senderUserId),
mlog.String("team_id", team.Id), mlog.String("team_id", team.Id),
mlog.String("retry_after", result.RetryAfter.String()), mlog.String("retry_after", result.RetryAfter.String()),
@@ -398,9 +403,9 @@ func (a *App) sendGuestInviteEmails(team *model.Team, channels []*model.Channel,
subject := utils.T("api.templates.invite_guest_subject", subject := utils.T("api.templates.invite_guest_subject",
map[string]interface{}{"SenderName": senderName, map[string]interface{}{"SenderName": senderName,
"TeamDisplayName": team.DisplayName, "TeamDisplayName": team.DisplayName,
"SiteName": a.ClientConfig()["SiteName"]}) "SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("invite_body", model.DEFAULT_LOCALE) bodyPage := es.newEmailTemplate("invite_body", model.DEFAULT_LOCALE)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title") bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body_guest.info", bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body_guest.info",
@@ -437,13 +442,13 @@ func (a *App) sendGuestInviteEmails(team *model.Team, channels []*model.Channel,
props["name"] = team.Name props["name"] = team.Name
data := model.MapToJson(props) data := model.MapToJson(props)
if err := a.Srv().Store.Token().Save(token); err != nil { if err := es.srv.Store.Token().Save(token); err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue continue
} }
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
if !*a.Config().EmailSettings.SendEmailNotifications { if !*es.srv.Config().EmailSettings.SendEmailNotifications {
mlog.Info("sending invitation ", mlog.String("to", invite), mlog.String("link", bodyPage.Props["Link"].(string))) mlog.Info("sending invitation ", mlog.String("to", invite), mlog.String("link", bodyPage.Props["Link"].(string)))
} }
@@ -456,15 +461,15 @@ func (a *App) sendGuestInviteEmails(team *model.Team, channels []*model.Channel,
} }
} }
if err := a.sendMailWithEmbeddedFiles(invite, subject, bodyPage.Render(), embeddedFiles); err != nil { if err := es.sendMailWithEmbeddedFiles(invite, subject, bodyPage.Render(), embeddedFiles); err != nil {
mlog.Error("Failed to send invite email successfully", mlog.Err(err)) mlog.Error("Failed to send invite email successfully", mlog.Err(err))
} }
} }
} }
} }
func (s *Server) newEmailTemplate(name, locale string) *utils.HTMLTemplate { func (es *EmailService) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
t := utils.NewHTMLTemplate(s.HTMLTemplates(), name) t := utils.NewHTMLTemplate(es.srv.HTMLTemplates(), name)
var localT i18n.TranslateFunc var localT i18n.TranslateFunc
if locale != "" { if locale != "" {
@@ -475,8 +480,8 @@ func (s *Server) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
t.Props["Footer"] = localT("api.templates.email_footer") t.Props["Footer"] = localT("api.templates.email_footer")
if *s.Config().EmailSettings.FeedbackOrganization != "" { if *es.srv.Config().EmailSettings.FeedbackOrganization != "" {
t.Props["Organization"] = localT("api.templates.email_organization") + *s.Config().EmailSettings.FeedbackOrganization t.Props["Organization"] = localT("api.templates.email_organization") + *es.srv.Config().EmailSettings.FeedbackOrganization
} else { } else {
t.Props["Organization"] = "" t.Props["Organization"] = ""
} }
@@ -484,84 +489,91 @@ func (s *Server) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
t.Props["EmailInfo1"] = localT("api.templates.email_info1") t.Props["EmailInfo1"] = localT("api.templates.email_info1")
t.Props["EmailInfo2"] = localT("api.templates.email_info2") t.Props["EmailInfo2"] = localT("api.templates.email_info2")
t.Props["EmailInfo3"] = localT("api.templates.email_info3", t.Props["EmailInfo3"] = localT("api.templates.email_info3",
map[string]interface{}{"SiteName": s.Config().TeamSettings.SiteName}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
t.Props["SupportEmail"] = *s.Config().SupportSettings.SupportEmail t.Props["SupportEmail"] = *es.srv.Config().SupportSettings.SupportEmail
return t return t
} }
func (a *App) newEmailTemplate(name, locale string) *utils.HTMLTemplate { func (es *EmailService) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError {
return a.Srv().newEmailTemplate(name, locale)
}
func (a *App) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
serverURL := condenseSiteURL(siteURL) serverURL := condenseSiteURL(siteURL)
subject := T("api.templates.deactivate_subject", subject := T("api.templates.deactivate_subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"], map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
"ServerURL": serverURL}) "ServerURL": serverURL})
bodyPage := a.newEmailTemplate("deactivate_body", locale) bodyPage := es.newEmailTemplate("deactivate_body", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": serverURL}) bodyPage.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": serverURL})
bodyPage.Props["Info"] = T("api.templates.deactivate_body.info", bodyPage.Props["Info"] = T("api.templates.deactivate_body.info",
map[string]interface{}{"SiteURL": siteURL}) map[string]interface{}{"SiteURL": siteURL})
bodyPage.Props["Warning"] = T("api.templates.deactivate_body.warning") bodyPage.Props["Warning"] = T("api.templates.deactivate_body.warning")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendDeactivateEmail", "api.user.send_deactivate_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("SendDeactivateEmail", "api.user.send_deactivate_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (a *App) SendRemoveExpiredLicenseEmail(email string, locale, siteURL string, licenseId string) *model.AppError { func (es *EmailService) SendRemoveExpiredLicenseEmail(email string, locale, siteURL string, licenseId string) *model.AppError {
T := utils.GetUserTranslations(locale) T := utils.GetUserTranslations(locale)
subject := T("api.templates.remove_expired_license.subject", subject := T("api.templates.remove_expired_license.subject",
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"]}) map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
bodyPage := a.newEmailTemplate("remove_expired_license", locale) bodyPage := es.newEmailTemplate("remove_expired_license", locale)
bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = T("api.templates.remove_expired_license.body.title") bodyPage.Props["Title"] = T("api.templates.remove_expired_license.body.title")
bodyPage.Props["Link"] = fmt.Sprintf("%s?id=%s", model.LICENSE_RENEWAL_LINK, licenseId) bodyPage.Props["Link"] = fmt.Sprintf("%s?id=%s", model.LICENSE_RENEWAL_LINK, licenseId)
bodyPage.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button") bodyPage.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button")
if err := a.sendMail(email, subject, bodyPage.Render()); err != nil { if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
func (s *Server) sendNotificationMail(to, subject, htmlBody string) *model.AppError { func (es *EmailService) sendNotificationMail(to, subject, htmlBody string) *model.AppError {
if !*s.Config().EmailSettings.SendEmailNotifications { if !*es.srv.Config().EmailSettings.SendEmailNotifications {
return nil return nil
} }
return s.sendMail(to, subject, htmlBody) return es.sendMail(to, subject, htmlBody)
} }
func (a *App) sendNotificationMail(to, subject, htmlBody string) *model.AppError { func (es *EmailService) sendMail(to, subject, htmlBody string) *model.AppError {
return a.Srv().sendNotificationMail(to, subject, htmlBody) license := es.srv.License()
return mailservice.SendMailUsingConfig(to, subject, htmlBody, es.srv.Config(), license != nil && *license.Features.Compliance)
} }
func (s *Server) sendMail(to, subject, htmlBody string) *model.AppError { func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) *model.AppError {
license := s.License() license := es.srv.License()
return mailservice.SendMailUsingConfig(to, subject, htmlBody, s.Config(), license != nil && *license.Features.Compliance) config := es.srv.Config()
}
func (a *App) sendMail(to, subject, htmlBody string) *model.AppError {
return a.Srv().sendMail(to, subject, htmlBody)
}
func (s *Server) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) *model.AppError {
license := s.License()
config := s.Config()
return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, config, license != nil && *license.Features.Compliance) return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, config, license != nil && *license.Features.Compliance)
} }
func (a *App) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) *model.AppError { func (es *EmailService) CreateVerifyEmailToken(userId string, newEmail string) (*model.Token, *model.AppError) {
return a.Srv().sendMailWithEmbeddedFiles(to, subject, htmlBody, embeddedFiles) tokenExtra := struct {
UserId string
Email string
}{
userId,
newEmail,
}
jsonData, err := json.Marshal(tokenExtra)
if err != nil {
return nil, model.NewAppError("CreateVerifyEmailToken", "api.user.create_email_token.error", nil, "", http.StatusInternalServerError)
}
token := model.NewToken(TOKEN_TYPE_VERIFY_EMAIL, string(jsonData))
if err := es.srv.Store.Token().Save(token); err != nil {
return nil, err
}
return token, nil
} }

Просмотреть файл

@@ -23,24 +23,24 @@ const (
EMAIL_BATCHING_TASK_NAME = "Email Batching" EMAIL_BATCHING_TASK_NAME = "Email Batching"
) )
func (s *Server) InitEmailBatching() { func (es *EmailService) InitEmailBatching() {
if *s.Config().EmailSettings.EnableEmailBatching { if *es.srv.Config().EmailSettings.EnableEmailBatching {
if s.EmailBatching == nil { if es.EmailBatching == nil {
s.EmailBatching = NewEmailBatchingJob(s, *s.Config().EmailSettings.EmailBatchingBufferSize) es.EmailBatching = NewEmailBatchingJob(es, *es.srv.Config().EmailSettings.EmailBatchingBufferSize)
} }
// note that we don't support changing EmailBatchingBufferSize without restarting the server // note that we don't support changing EmailBatchingBufferSize without restarting the server
s.EmailBatching.Start() es.EmailBatching.Start()
} }
} }
func (a *App) AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError { func (es *EmailService) AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError {
if !*a.Config().EmailSettings.EnableEmailBatching { if !*es.srv.Config().EmailSettings.EnableEmailBatching {
return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.disabled.app_error", nil, "", http.StatusNotImplemented) return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.disabled.app_error", nil, "", http.StatusNotImplemented)
} }
if !a.Srv().EmailBatching.Add(user, post, team) { if !es.EmailBatching.Add(user, post, team) {
mlog.Error("Email batching job's receiving channel was full. Please increase the EmailBatchingBufferSize.") mlog.Error("Email batching job's receiving channel was full. Please increase the EmailBatchingBufferSize.")
return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.channel_full.app_error", nil, "", http.StatusInternalServerError) return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.channel_full.app_error", nil, "", http.StatusInternalServerError)
} }
@@ -62,9 +62,9 @@ type EmailBatchingJob struct {
taskMutex sync.Mutex taskMutex sync.Mutex
} }
func NewEmailBatchingJob(s *Server, bufferSize int) *EmailBatchingJob { func NewEmailBatchingJob(es *EmailService, bufferSize int) *EmailBatchingJob {
return &EmailBatchingJob{ return &EmailBatchingJob{
server: s, server: es.srv,
newNotifications: make(chan *batchedNotification, bufferSize), newNotifications: make(chan *batchedNotification, bufferSize),
pendingNotifications: make(map[string][]*batchedNotification), pendingNotifications: make(map[string][]*batchedNotification),
} }
@@ -105,7 +105,7 @@ func (job *EmailBatchingJob) CheckPendingEmails() {
// it's a bit weird to pass the send email function through here, but it makes it so that we can test // it's a bit weird to pass the send email function through here, but it makes it so that we can test
// without actually sending emails // without actually sending emails
job.checkPendingNotifications(time.Now(), job.server.sendBatchedEmailNotification) job.checkPendingNotifications(time.Now(), job.server.EmailService.sendBatchedEmailNotification)
mlog.Debug("Email batching job ran. Some users still have notifications pending.", mlog.Int("number_of_users", len(job.pendingNotifications))) mlog.Debug("Email batching job ran. Some users still have notifications pending.", mlog.Int("number_of_users", len(job.pendingNotifications)))
} }
@@ -194,68 +194,68 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
} }
} }
func (s *Server) sendBatchedEmailNotification(userId string, notifications []*batchedNotification) { func (es *EmailService) sendBatchedEmailNotification(userId string, notifications []*batchedNotification) {
user, err := s.Store.User().Get(userId) user, err := es.srv.Store.User().Get(userId)
if err != nil { if err != nil {
mlog.Warn("Unable to find recipient for batched email notification") mlog.Warn("Unable to find recipient for batched email notification")
return return
} }
translateFunc := utils.GetUserTranslations(user.Locale) translateFunc := utils.GetUserTranslations(user.Locale)
displayNameFormat := *s.Config().TeamSettings.TeammateNameDisplay displayNameFormat := *es.srv.Config().TeamSettings.TeammateNameDisplay
var contents string var contents string
for _, notification := range notifications { for _, notification := range notifications {
sender, err := s.Store.User().Get(notification.post.UserId) sender, err := es.srv.Store.User().Get(notification.post.UserId)
if err != nil { if err != nil {
mlog.Warn("Unable to find sender of post for batched email notification") mlog.Warn("Unable to find sender of post for batched email notification")
continue continue
} }
channel, errCh := s.Store.Channel().Get(notification.post.ChannelId, true) channel, errCh := es.srv.Store.Channel().Get(notification.post.ChannelId, true)
if errCh != nil { if errCh != nil {
mlog.Warn("Unable to find channel of post for batched email notification") mlog.Warn("Unable to find channel of post for batched email notification")
continue continue
} }
emailNotificationContentsType := model.EMAIL_NOTIFICATION_CONTENTS_FULL emailNotificationContentsType := model.EMAIL_NOTIFICATION_CONTENTS_FULL
if license := s.License(); license != nil && *license.Features.EmailNotificationContents { if license := es.srv.License(); license != nil && *license.Features.EmailNotificationContents {
emailNotificationContentsType = *s.Config().EmailSettings.EmailNotificationContentsType emailNotificationContentsType = *es.srv.Config().EmailSettings.EmailNotificationContentsType
} }
contents += s.renderBatchedPost(notification, channel, sender, *s.Config().ServiceSettings.SiteURL, displayNameFormat, translateFunc, user.Locale, emailNotificationContentsType) contents += es.renderBatchedPost(notification, channel, sender, *es.srv.Config().ServiceSettings.SiteURL, displayNameFormat, translateFunc, user.Locale, emailNotificationContentsType)
} }
tm := time.Unix(notifications[0].post.CreateAt/1000, 0) tm := time.Unix(notifications[0].post.CreateAt/1000, 0)
subject := translateFunc("api.email_batching.send_batched_email_notification.subject", len(notifications), map[string]interface{}{ subject := translateFunc("api.email_batching.send_batched_email_notification.subject", len(notifications), map[string]interface{}{
"SiteName": s.Config().TeamSettings.SiteName, "SiteName": es.srv.Config().TeamSettings.SiteName,
"Year": tm.Year(), "Year": tm.Year(),
"Month": translateFunc(tm.Month().String()), "Month": translateFunc(tm.Month().String()),
"Day": tm.Day(), "Day": tm.Day(),
}) })
body := s.newEmailTemplate("post_batched_body", user.Locale) body := es.newEmailTemplate("post_batched_body", user.Locale)
body.Props["SiteURL"] = *s.Config().ServiceSettings.SiteURL body.Props["SiteURL"] = *es.srv.Config().ServiceSettings.SiteURL
body.Props["Posts"] = template.HTML(contents) body.Props["Posts"] = template.HTML(contents)
body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications)) body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
if err := s.sendNotificationMail(user.Email, subject, body.Render()); err != nil { if err := es.sendNotificationMail(user.Email, subject, body.Render()); err != nil {
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(err)) mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(err))
} }
} }
func (s *Server) renderBatchedPost(notification *batchedNotification, channel *model.Channel, sender *model.User, siteURL string, displayNameFormat string, translateFunc i18n.TranslateFunc, userLocale string, emailNotificationContentsType string) string { func (es *EmailService) renderBatchedPost(notification *batchedNotification, channel *model.Channel, sender *model.User, siteURL string, displayNameFormat string, translateFunc i18n.TranslateFunc, userLocale string, emailNotificationContentsType string) string {
// don't include message contents if email notification contents type is set to generic // don't include message contents if email notification contents type is set to generic
var template *utils.HTMLTemplate var template *utils.HTMLTemplate
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL { if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
template = s.newEmailTemplate("post_batched_post_full", userLocale) template = es.newEmailTemplate("post_batched_post_full", userLocale)
} else { } else {
template = s.newEmailTemplate("post_batched_post_generic", userLocale) template = es.newEmailTemplate("post_batched_post_generic", userLocale)
} }
template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post") template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
template.Props["PostMessage"] = s.GetMessageForNotification(notification.post, translateFunc) template.Props["PostMessage"] = es.srv.GetMessageForNotification(notification.post, translateFunc)
template.Props["PostLink"] = siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id template.Props["PostLink"] = siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
template.Props["SenderName"] = sender.GetDisplayName(displayNameFormat) template.Props["SenderName"] = sender.GetDisplayName(displayNameFormat)

Просмотреть файл

@@ -23,7 +23,7 @@ func TestHandleNewNotifications(t *testing.T) {
id3 := model.NewId() id3 := model.NewId()
// test queueing of received posts by user // test queueing of received posts by user
job := NewEmailBatchingJob(th.Server, 128) job := NewEmailBatchingJob(th.Server.EmailService, 128)
job.handleNewNotifications() job.handleNewNotifications()
@@ -58,7 +58,7 @@ func TestHandleNewNotifications(t *testing.T) {
require.Len(t, job.pendingNotifications[id3], 1, "should have received 1 post for user3") require.Len(t, job.pendingNotifications[id3], 1, "should have received 1 post for user3")
// test ordering of received posts // test ordering of received posts
job = NewEmailBatchingJob(th.Server, 128) job = NewEmailBatchingJob(th.Server.EmailService, 128)
job.Add(&model.User{Id: id1}, &model.Post{UserId: id1, Message: "test1"}, &model.Team{Name: "team"}) job.Add(&model.User{Id: id1}, &model.Post{UserId: id1, Message: "test1"}, &model.Team{Name: "team"})
job.Add(&model.User{Id: id1}, &model.Post{UserId: id1, Message: "test2"}, &model.Team{Name: "team"}) job.Add(&model.User{Id: id1}, &model.Post{UserId: id1, Message: "test2"}, &model.Team{Name: "team"})
@@ -77,7 +77,7 @@ func TestCheckPendingNotifications(t *testing.T) {
th := Setup(t).InitBasic() th := Setup(t).InitBasic()
defer th.TearDown() defer th.TearDown()
job := NewEmailBatchingJob(th.Server, 128) job := NewEmailBatchingJob(th.Server.EmailService, 128)
job.pendingNotifications[th.BasicUser.Id] = []*batchedNotification{ job.pendingNotifications[th.BasicUser.Id] = []*batchedNotification{
{ {
post: &model.Post{ post: &model.Post{
@@ -205,7 +205,7 @@ func TestCheckPendingNotificationsDefaultInterval(t *testing.T) {
th := Setup(t).InitBasic() th := Setup(t).InitBasic()
defer th.TearDown() defer th.TearDown()
job := NewEmailBatchingJob(th.Server, 128) job := NewEmailBatchingJob(th.Server.EmailService, 128)
// bypasses recent user activity check // bypasses recent user activity check
channelMember, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id) channelMember, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
@@ -243,7 +243,7 @@ func TestCheckPendingNotificationsCantParseInterval(t *testing.T) {
th := Setup(t).InitBasic() th := Setup(t).InitBasic()
defer th.TearDown() defer th.TearDown()
job := NewEmailBatchingJob(th.Server, 128) job := NewEmailBatchingJob(th.Server.EmailService, 128)
// bypasses recent user activity check // bypasses recent user activity check
channelMember, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id) channelMember, err := th.App.Srv().Store.Channel().GetMember(th.BasicChannel.Id, th.BasicUser.Id)
@@ -304,7 +304,7 @@ func TestRenderBatchedPostGeneric(t *testing.T) {
return translationID return translationID
} }
var rendered = th.Server.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_GENERIC) var rendered = th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_GENERIC)
require.NotContains(t, rendered, post.Message, "Rendered email should not contain post contents when email notification contents type is set to Generic.") require.NotContains(t, rendered, post.Message, "Rendered email should not contain post contents when email notification contents type is set to Generic.")
} }
@@ -329,6 +329,6 @@ func TestRenderBatchedPostFull(t *testing.T) {
return translationID return translationID
} }
var rendered = th.Server.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_FULL) var rendered = th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_FULL)
require.Contains(t, rendered, post.Message, "Rendered email should contain post contents when email notification contents type is set to Full.") require.Contains(t, rendered, post.Message, "Rendered email should contain post contents when email notification contents type is set to Full.")
} }

Просмотреть файл

@@ -107,7 +107,7 @@ func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword
} }
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, a.GetSiteURL()); err != nil {
mlog.Error(err.Error()) mlog.Error(err.Error())
} }
}) })
@@ -153,7 +153,7 @@ func (a *App) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (
T := utils.GetUserTranslations(user.Locale) T := utils.GetUserTranslations(user.Locale)
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
mlog.Error(err.Error()) mlog.Error(err.Error())
} }
}) })

Просмотреть файл

@@ -57,7 +57,7 @@ func (a *App) sendNotificationEmail(notification *PostNotification, user *model.
} }
if sendBatched { if sendBatched {
if err := a.AddNotificationEmailToBatch(user, post, team); err == nil { if err := a.Srv().EmailService.AddNotificationEmailToBatch(user, post, team); err == nil {
return nil return nil
} }
} }
@@ -99,7 +99,7 @@ func (a *App) sendNotificationEmail(notification *PostNotification, user *model.
var bodyText = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc) var bodyText = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc)
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.sendNotificationMail(user.Email, html.UnescapeString(subjectText), bodyText); err != nil { if err := a.Srv().EmailService.sendNotificationMail(user.Email, html.UnescapeString(subjectText), bodyText); err != nil {
mlog.Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(err)) mlog.Error("Error while sending the email", mlog.String("user_email", user.Email), mlog.Err(err))
} }
}) })
@@ -166,13 +166,13 @@ func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post,
// only include message contents in notification email if email notification contents type is set to full // only include message contents in notification email if email notification contents type is set to full
var bodyPage *utils.HTMLTemplate var bodyPage *utils.HTMLTemplate
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL { if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
bodyPage = a.newEmailTemplate("post_body_full", recipient.Locale) bodyPage = a.Srv().EmailService.newEmailTemplate("post_body_full", recipient.Locale)
postMessage := a.GetMessageForNotification(post, translateFunc) postMessage := a.GetMessageForNotification(post, translateFunc)
postMessage = html.EscapeString(postMessage) postMessage = html.EscapeString(postMessage)
normalizedPostMessage := a.generateHyperlinkForChannels(postMessage, teamName, landingURL) normalizedPostMessage := a.generateHyperlinkForChannels(postMessage, teamName, landingURL)
bodyPage.Props["PostMessage"] = template.HTML(normalizedPostMessage) bodyPage.Props["PostMessage"] = template.HTML(normalizedPostMessage)
} else { } else {
bodyPage = a.newEmailTemplate("post_body_generic", recipient.Locale) bodyPage = a.Srv().EmailService.newEmailTemplate("post_body_generic", recipient.Locale)
} }
bodyPage.Props["SiteURL"] = a.GetSiteURL() bodyPage.Props["SiteURL"] = a.GetSiteURL()

Просмотреть файл

@@ -584,7 +584,7 @@ func (a *App) CompleteSwitchWithOAuth(service string, userData io.Reader, email
} }
a.Srv().Go(func() { a.Srv().Go(func() {
if err = a.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil { if err = a.Srv().EmailService.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil {
mlog.Error("error sending signin change email", mlog.Err(err)) mlog.Error("error sending signin change email", mlog.Err(err))
} }
}) })
@@ -853,7 +853,7 @@ func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *
T := utils.GetUserTranslations(user.Locale) T := utils.GetUserTranslations(user.Locale)
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
mlog.Error("error sending signin change email", mlog.Err(err)) mlog.Error("error sending signin change email", mlog.Err(err))
} }
}) })

Просмотреть файл

@@ -170,28 +170,6 @@ func (a *OpenTracingAppLayer) AddDirectChannels(teamId string, user *model.User)
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddNotificationEmailToBatch")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.AddNotificationEmailToBatch(user, post, team)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) AddPublicKey(name string, key io.Reader) *model.AppError { func (a *OpenTracingAppLayer) AddPublicKey(name string, key io.Reader) *model.AppError {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddPublicKey") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddPublicKey")
@@ -2245,28 +2223,6 @@ func (a *OpenTracingAppLayer) CreateUserWithToken(user *model.User, token *model
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) CreateVerifyEmailToken(userId string, newEmail string) (*model.Token, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateVerifyEmailToken")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CreateVerifyEmailToken(userId, newEmail)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateWebhookPost(userId string, channel *model.Channel, text string, overrideUsername string, overrideIconUrl string, overrideIconEmoji string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) { func (a *OpenTracingAppLayer) CreateWebhookPost(userId string, channel *model.Channel, text string, overrideUsername string, overrideIconUrl string, overrideIconEmoji string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateWebhookPost") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateWebhookPost")
@@ -12472,28 +12428,6 @@ func (a *OpenTracingAppLayer) SendAutoResponseIfNecessary(channel *model.Channel
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) SendDeactivateAccountEmail(email string, locale string, siteURL string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendDeactivateAccountEmail")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.SendDeactivateAccountEmail(email, locale, siteURL)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) SendEmailVerification(user *model.User, newEmail string) *model.AppError { func (a *OpenTracingAppLayer) SendEmailVerification(user *model.User, newEmail string) *model.AppError {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendEmailVerification") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendEmailVerification")
@@ -12533,21 +12467,6 @@ func (a *OpenTracingAppLayer) SendEphemeralPost(userId string, post *model.Post)
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendInviteEmails")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
a.app.SendInviteEmails(team, senderName, senderUserId, invites, siteURL)
}
func (a *OpenTracingAppLayer) SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList, setOnline bool) ([]string, error) { func (a *OpenTracingAppLayer) SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User, parentPostList *model.PostList, setOnline bool) ([]string, error) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendNotifications") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendNotifications")
@@ -12592,72 +12511,6 @@ func (a *OpenTracingAppLayer) SendPasswordReset(email string, siteURL string) (b
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) SendPasswordResetEmail(email string, token *model.Token, locale string, siteURL string) (bool, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendPasswordResetEmail")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.SendPasswordResetEmail(email, token, locale, siteURL)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) SendRemoveExpiredLicenseEmail(email string, locale string, siteURL string, licenseId string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendRemoveExpiredLicenseEmail")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.SendRemoveExpiredLicenseEmail(email, locale, siteURL, licenseId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) SendSignInChangeEmail(email string, method string, locale string, siteURL string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendSignInChangeEmail")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.SendSignInChangeEmail(email, method, locale, siteURL)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId string, destinationPluginId string) { func (a *OpenTracingAppLayer) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId string, destinationPluginId string) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ServeInterPluginRequest") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ServeInterPluginRequest")

Просмотреть файл

@@ -704,7 +704,7 @@ func (api *PluginAPI) SendMail(to, subject, htmlBody string) *model.AppError {
return model.NewAppError("SendMail", "plugin_api.send_mail.missing_htmlbody", nil, "", http.StatusBadRequest) return model.NewAppError("SendMail", "plugin_api.send_mail.missing_htmlbody", nil, "", http.StatusBadRequest)
} }
return api.app.sendNotificationMail(to, subject, htmlBody) return api.app.Srv().EmailService.sendNotificationMail(to, subject, htmlBody)
} }
// Plugin Section // Plugin Section

Просмотреть файл

@@ -26,7 +26,6 @@ import (
"github.com/rs/cors" "github.com/rs/cors"
rudder "github.com/rudderlabs/analytics-go" rudder "github.com/rudderlabs/analytics-go"
"github.com/throttled/throttled"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
"github.com/mattermost/mattermost-server/v5/audit" "github.com/mattermost/mattermost-server/v5/audit"
@@ -89,8 +88,7 @@ type Server struct {
PluginConfigListenerId string PluginConfigListenerId string
PluginsLock sync.RWMutex PluginsLock sync.RWMutex
EmailBatching *EmailBatchingJob EmailService *EmailService
EmailRateLimiter *throttled.GCRARateLimiter
hubs []*Hub hubs []*Hub
hashSeed maphash.Seed hashSeed maphash.Seed
@@ -312,10 +310,6 @@ func NewServer(options ...Option) (*Server, error) {
}) })
if err := s.setupInviteEmailRateLimiting(); err != nil {
return nil, err
}
s.initEnterprise() s.initEnterprise()
if s.newStore == nil { if s.newStore == nil {
@@ -356,6 +350,12 @@ func NewServer(options ...Option) (*Server, error) {
s.Store = s.newStore() s.Store = s.newStore()
emailService, err := NewEmailService(s)
if err != nil {
return nil, errors.Wrapf(err, "unable to initialize email service")
}
s.EmailService = emailService
if model.BuildEnterpriseReady == "true" { if model.BuildEnterpriseReady == "true" {
s.LoadLicense() s.LoadLicense()
} }
@@ -366,19 +366,19 @@ func NewServer(options ...Option) (*Server, error) {
s.Cluster.StartInterNodeCommunication() s.Cluster.StartInterNodeCommunication()
} }
if err := s.ensureAsymmetricSigningKey(); err != nil { if err = s.ensureAsymmetricSigningKey(); err != nil {
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
} }
if err := s.ensurePostActionCookieSecret(); err != nil { if err = s.ensurePostActionCookieSecret(); err != nil {
return nil, errors.Wrapf(err, "unable to ensure PostAction cookie secret") return nil, errors.Wrapf(err, "unable to ensure PostAction cookie secret")
} }
if err := s.ensureInstallationDate(); err != nil { if err = s.ensureInstallationDate(); err != nil {
return nil, errors.Wrapf(err, "unable to ensure installation date") return nil, errors.Wrapf(err, "unable to ensure installation date")
} }
if err := s.ensureFirstServerRunTimestamp(); err != nil { if err = s.ensureFirstServerRunTimestamp(); err != nil {
return nil, errors.Wrapf(err, "unable to ensure first run timestamp") return nil, errors.Wrapf(err, "unable to ensure first run timestamp")
} }
@@ -417,11 +417,11 @@ func NewServer(options ...Option) (*Server, error) {
handlers: make(map[string]webSocketHandler), handlers: make(map[string]webSocketHandler),
} }
if err := mailservice.TestConnection(s.Config()); err != nil { if appErr := mailservice.TestConnection(s.Config()); appErr != nil {
mlog.Error("Mail server connection test is failed: " + err.Message) mlog.Error("Mail server connection test is failed: " + appErr.Message)
} }
if _, err := url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil { if _, err = url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil {
mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: http://about.mattermost.com/default-site-url") mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: http://about.mattermost.com/default-site-url")
} }
@@ -437,9 +437,8 @@ func NewServer(options ...Option) (*Server, error) {
s.timezones = timezones.New() s.timezones = timezones.New()
// Start email batching because it's not like the other jobs // Start email batching because it's not like the other jobs
s.InitEmailBatching()
s.AddConfigListener(func(_, _ *model.Config) { s.AddConfigListener(func(_, _ *model.Config) {
s.InitEmailBatching() s.EmailService.InitEmailBatching()
}) })
// Start plugin health check job // Start plugin health check job
@@ -496,8 +495,8 @@ func NewServer(options ...Option) (*Server, error) {
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true }) s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
} }
if err := s.Store.Status().ResetAll(); err != nil { if appErr = s.Store.Status().ResetAll(); appErr != nil {
mlog.Error("Error to reset the server status.", mlog.Err(err)) mlog.Error("Error to reset the server status.", mlog.Err(appErr))
} }
if s.startMetrics && s.Metrics != nil { if s.startMetrics && s.Metrics != nil {
@@ -1073,7 +1072,7 @@ func doLicenseExpirationCheck(a *App) {
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email)) mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.SendRemoveExpiredLicenseEmail(user.Email, user.Locale, *a.Config().ServiceSettings.SiteURL, license.Id); err != nil { if err := a.Srv().EmailService.SendRemoveExpiredLicenseEmail(user.Email, user.Locale, *a.Config().ServiceSettings.SiteURL, license.Id); err != nil {
mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err)) mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err))
} }
}) })

Просмотреть файл

@@ -376,7 +376,7 @@ func (a *App) CreateUserAccessToken(token *model.UserAccessToken) (*model.UserAc
// Don't send emails to bot users. // Don't send emails to bot users.
if !user.IsBot { if !user.IsBot {
if err := a.sendUserAccessTokenAddedEmail(user.Email, user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendUserAccessTokenAddedEmail(user.Email, user.Locale, a.GetSiteURL()); err != nil {
a.Log().Error("Unable to send user access token added email", mlog.Err(err), mlog.String("user_id", user.Id)) a.Log().Error("Unable to send user access token added email", mlog.Err(err), mlog.String("user_id", user.Id))
} }
} }

Просмотреть файл

@@ -1148,7 +1148,7 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderI
if len(goodEmails) > 0 { if len(goodEmails) > 0 {
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
a.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL()) a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL())
} }
return inviteListWithErrors, nil return inviteListWithErrors, nil
@@ -1231,7 +1231,11 @@ func (a *App) InviteGuestsToChannelsGracefully(teamId string, guestsInvite *mode
if len(goodEmails) > 0 { if len(goodEmails) > 0 {
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
a.sendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL(), guestsInvite.Message) senderProfileImage, _, err := a.GetProfileImage(user)
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
a.Srv().EmailService.sendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), guestsInvite.Message)
} }
return inviteListWithErrors, nil return inviteListWithErrors, nil
@@ -1268,7 +1272,7 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string)
} }
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
a.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL()) a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL())
return nil return nil
} }
@@ -1292,12 +1296,15 @@ func (a *App) InviteGuestsToChannels(teamId string, guestsInvite *model.GuestsIn
if len(invalidEmailList) > 0 { if len(invalidEmailList) > 0 {
s := strings.Join(invalidEmailList, ", ") s := strings.Join(invalidEmailList, ", ")
err := model.NewAppError("InviteGuestsToChannels", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest) return model.NewAppError("InviteGuestsToChannels", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest)
return err
} }
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
a.sendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, guestsInvite.Emails, a.GetSiteURL(), guestsInvite.Message) senderProfileImage, _, err := a.GetProfileImage(user)
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
a.Srv().EmailService.sendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, guestsInvite.Emails, a.GetSiteURL(), guestsInvite.Message)
return nil return nil
} }

Просмотреть файл

@@ -139,7 +139,7 @@ func (a *App) CreateUserWithInviteId(user *model.User, inviteId string) (*model.
a.AddDirectChannels(team.Id, ruser) a.AddDirectChannels(team.Id, ruser)
if err := a.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send welcome email on create user with inviteId", mlog.Err(err)) mlog.Error("Failed to send welcome email on create user with inviteId", mlog.Err(err))
} }
@@ -152,7 +152,7 @@ func (a *App) CreateUserAsAdmin(user *model.User) (*model.User, *model.AppError)
return nil, err return nil, err
} }
if err := a.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send welcome email on create admin user", mlog.Err(err)) mlog.Error("Failed to send welcome email on create admin user", mlog.Err(err))
} }
@@ -176,7 +176,7 @@ func (a *App) CreateUserFromSignup(user *model.User) (*model.User, *model.AppErr
return nil, err return nil, err
} }
if err := a.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send welcome email on create user from signup", mlog.Err(err)) mlog.Error("Failed to send welcome email on create user from signup", mlog.Err(err))
} }
@@ -1165,7 +1165,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
}) })
} else { } else {
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.sendEmailChangeEmail(userUpdate.Old.Email, userUpdate.New.Email, userUpdate.New.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendEmailChangeEmail(userUpdate.Old.Email, userUpdate.New.Email, userUpdate.New.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send email change email", mlog.Err(err)) mlog.Error("Failed to send email change email", mlog.Err(err))
} }
}) })
@@ -1174,7 +1174,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
if userUpdate.New.Username != userUpdate.Old.Username { if userUpdate.New.Username != userUpdate.Old.Username {
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.sendChangeUsernameEmail(userUpdate.Old.Username, userUpdate.New.Username, userUpdate.New.Email, userUpdate.New.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendChangeUsernameEmail(userUpdate.Old.Username, userUpdate.New.Username, userUpdate.New.Email, userUpdate.New.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send change username email", mlog.Err(err)) mlog.Error("Failed to send change username email", mlog.Err(err))
} }
}) })
@@ -1233,7 +1233,7 @@ func (a *App) UpdateMfa(activate bool, userId, token string) *model.AppError {
return return
} }
if err := a.sendMfaChangeEmail(user.Email, activate, user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendMfaChangeEmail(user.Email, activate, user.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send mfa change email", mlog.Err(err)) mlog.Error("Failed to send mfa change email", mlog.Err(err))
} }
}) })
@@ -1272,7 +1272,7 @@ func (a *App) UpdatePasswordSendEmail(user *model.User, newPassword, method stri
} }
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.sendPasswordChangeEmail(user.Email, method, user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendPasswordChangeEmail(user.Email, method, user.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send password change email", mlog.Err(err)) mlog.Error("Failed to send password change email", mlog.Err(err))
} }
}) })
@@ -1340,7 +1340,7 @@ func (a *App) SendPasswordReset(email string, siteURL string) (bool, *model.AppE
return false, err return false, err
} }
return a.SendPasswordResetEmail(user.Email, token, user.Locale, siteURL) return a.Srv().EmailService.SendPasswordResetEmail(user.Email, token, user.Locale, siteURL)
} }
func (a *App) CreatePasswordRecoveryToken(userId, email string) (*model.Token, *model.AppError) { func (a *App) CreatePasswordRecoveryToken(userId, email string) (*model.Token, *model.AppError) {
@@ -1558,15 +1558,15 @@ func (a *App) PermanentDeleteAllUsers() *model.AppError {
} }
func (a *App) SendEmailVerification(user *model.User, newEmail string) *model.AppError { func (a *App) SendEmailVerification(user *model.User, newEmail string) *model.AppError {
token, err := a.CreateVerifyEmailToken(user.Id, newEmail) token, err := a.Srv().EmailService.CreateVerifyEmailToken(user.Id, newEmail)
if err != nil { if err != nil {
return err return err
} }
if _, err := a.GetStatus(user.Id); err != nil { if _, err := a.GetStatus(user.Id); err != nil {
return a.sendVerifyEmail(newEmail, user.Locale, a.GetSiteURL(), token.Token) return a.Srv().EmailService.sendVerifyEmail(newEmail, user.Locale, a.GetSiteURL(), token.Token)
} }
return a.sendEmailChangeVerifyEmail(newEmail, user.Locale, a.GetSiteURL(), token.Token) return a.Srv().EmailService.sendEmailChangeVerifyEmail(newEmail, user.Locale, a.GetSiteURL(), token.Token)
} }
func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppError { func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppError {
@@ -1600,7 +1600,7 @@ func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppErr
if user.Email != tokenData.Email { if user.Email != tokenData.Email {
a.Srv().Go(func() { a.Srv().Go(func() {
if err := a.sendEmailChangeEmail(user.Email, tokenData.Email, user.Locale, a.GetSiteURL()); err != nil { if err := a.Srv().EmailService.sendEmailChangeEmail(user.Email, tokenData.Email, user.Locale, a.GetSiteURL()); err != nil {
mlog.Error("Failed to send email change email", mlog.Err(err)) mlog.Error("Failed to send email change email", mlog.Err(err))
} }
}) })
@@ -1613,29 +1613,6 @@ func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppErr
return nil return nil
} }
func (a *App) CreateVerifyEmailToken(userId string, newEmail string) (*model.Token, *model.AppError) {
tokenExtra := struct {
UserId string
Email string
}{
userId,
newEmail,
}
jsonData, err := json.Marshal(tokenExtra)
if err != nil {
return nil, model.NewAppError("CreateVerifyEmailToken", "api.user.create_email_token.error", nil, "", http.StatusInternalServerError)
}
token := model.NewToken(TOKEN_TYPE_VERIFY_EMAIL, string(jsonData))
if err := a.Srv().Store.Token().Save(token); err != nil {
return nil, err
}
return token, nil
}
func (a *App) GetVerifyEmailToken(token string) (*model.Token, *model.AppError) { func (a *App) GetVerifyEmailToken(token string) (*model.Token, *model.AppError) {
rtoken, err := a.Srv().Store.Token().GetByToken(token) rtoken, err := a.Srv().Store.Token().GetByToken(token)
if err != nil { if err != nil {

Просмотреть файл

@@ -381,7 +381,7 @@ func TestUpdateUserEmail(t *testing.T) {
assert.Equal(t, currentEmail, user2.Email) assert.Equal(t, currentEmail, user2.Email)
assert.True(t, user2.EmailVerified) assert.True(t, user2.EmailVerified)
token, err := th.App.CreateVerifyEmailToken(user2.Id, newEmail) token, err := th.App.Srv().EmailService.CreateVerifyEmailToken(user2.Id, newEmail)
assert.Nil(t, err) assert.Nil(t, err)
err = th.App.VerifyEmailFromToken(token.Token) err = th.App.VerifyEmailFromToken(token.Token)

Просмотреть файл

@@ -623,7 +623,7 @@ func inviteUser(a *app.App, email string, team *model.Team, teamArg string) erro
return fmt.Errorf("Email invites are disabled.") return fmt.Errorf("Email invites are disabled.")
} }
a.SendInviteEmails(team, "Administrator", "Mattermost CLI "+model.NewId(), invites, *a.Config().ServiceSettings.SiteURL) a.Srv().EmailService.SendInviteEmails(team, "Administrator", "Mattermost CLI "+model.NewId(), invites, *a.Config().ServiceSettings.SiteURL)
CommandPrettyPrintln("Invites may or may not have been sent.") CommandPrettyPrintln("Invites may or may not have been sent.")
auditRec := a.MakeAuditRecord("inviteUser", audit.Success) auditRec := a.MakeAuditRecord("inviteUser", audit.Success)

Просмотреть файл

@@ -137,7 +137,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user") c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")
c.App.Srv().Go(func() { c.App.Srv().Go(func() {
if err = c.App.SendSignInChangeEmail(user.Email, strings.Title(model.USER_AUTH_SERVICE_SAML)+" SSO", user.Locale, c.App.GetSiteURL()); err != nil { if err = c.App.Srv().EmailService.SendSignInChangeEmail(user.Email, strings.Title(model.USER_AUTH_SERVICE_SAML)+" SSO", user.Locale, c.App.GetSiteURL()); err != nil {
mlog.Error(err.Error()) mlog.Error(err.Error())
} }
}) })