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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
10f5a8890c
Коммит
b977017ca7
@@ -84,7 +84,7 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
auditRec.AddMeta("errors", errList)
|
||||
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
|
||||
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)
|
||||
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)
|
||||
}
|
||||
auditRec.Success()
|
||||
|
||||
@@ -1185,7 +1185,7 @@ func updateUserActive(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if isSelfDeactive {
|
||||
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())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3068,7 +3068,7 @@ func TestVerifyUserEmail(t *testing.T) {
|
||||
|
||||
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")
|
||||
|
||||
_, 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)
|
||||
AddConfigListener(listener func(*model.Config, *model.Config)) string
|
||||
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
|
||||
AddSamlPrivateCertificate(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)
|
||||
CreateUserWithInviteId(user *model.User, inviteId string) (*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)
|
||||
DataRetention() einterfaces.DataRetentionInterface
|
||||
DeactivateGuests() *model.AppError
|
||||
@@ -852,15 +850,10 @@ type AppIface interface {
|
||||
SendAckToPushProxy(ack *model.PushNotificationAck) error
|
||||
SendAutoResponse(channel *model.Channel, receiver *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
|
||||
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)
|
||||
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)
|
||||
ServePluginRequest(w http.ResponseWriter, r *http.Request)
|
||||
Session() *model.Session
|
||||
|
||||
264
app/email.go
264
app/email.go
@@ -5,6 +5,7 @@ package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
@@ -39,7 +40,22 @@ func condenseSiteURL(siteURL string) string {
|
||||
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)
|
||||
if err != nil {
|
||||
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.")
|
||||
}
|
||||
|
||||
s.EmailRateLimiter = rateLimiter
|
||||
es.EmailRateLimiter = rateLimiter
|
||||
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)
|
||||
|
||||
subject := T("api.templates.username_change_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||
map[string]interface{}{"SiteName": es.srv.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["Title"] = T("api.templates.username_change_body.title")
|
||||
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")
|
||||
|
||||
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 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)
|
||||
|
||||
link := fmt.Sprintf("%s/do_verify_email?token=%s&email=%s", siteURL, token, url.QueryEscape(newUserEmail))
|
||||
|
||||
subject := T("api.templates.email_change_verify_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||
map[string]interface{}{"SiteName": es.srv.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["Title"] = T("api.templates.email_change_verify_body.title")
|
||||
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["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 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)
|
||||
|
||||
subject := T("api.templates.email_change_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||
map[string]interface{}{"SiteName": es.srv.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["Title"] = T("api.templates.email_change_body.title")
|
||||
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")
|
||||
|
||||
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 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)
|
||||
|
||||
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)
|
||||
|
||||
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["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": serverURL})
|
||||
bodyPage.Props["Info"] = T("api.templates.verify_body.info")
|
||||
bodyPage.Props["VerifyUrl"] = link
|
||||
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 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)
|
||||
|
||||
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["Title"] = T("api.templates.signin_change_email.body.title")
|
||||
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")
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (a *App) sendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError {
|
||||
if !*a.Config().EmailSettings.SendEmailNotifications && !*a.Config().EmailSettings.RequireEmailVerification {
|
||||
func (es *EmailService) sendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -179,10 +195,10 @@ func (a *App) sendWelcomeEmail(userId string, email string, verified bool, local
|
||||
serverURL := condenseSiteURL(siteURL)
|
||||
|
||||
subject := T("api.templates.welcome_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"ServerURL": serverURL})
|
||||
|
||||
bodyPage := a.newEmailTemplate("welcome_body", locale)
|
||||
bodyPage := es.newEmailTemplate("welcome_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": serverURL})
|
||||
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["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["AppDownloadLink"] = *a.Config().NativeAppSettings.AppDownloadLink
|
||||
bodyPage.Props["AppDownloadLink"] = *es.srv.Config().NativeAppSettings.AppDownloadLink
|
||||
}
|
||||
|
||||
if !verified && *a.Config().EmailSettings.RequireEmailVerification {
|
||||
token, err := a.CreateVerifyEmailToken(userId, email)
|
||||
if !verified && *es.srv.Config().EmailSettings.RequireEmailVerification {
|
||||
token, err := es.CreateVerifyEmailToken(userId, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,63 +221,63 @@ func (a *App) sendWelcomeEmail(userId string, email string, verified bool, local
|
||||
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 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)
|
||||
|
||||
subject := T("api.templates.password_change_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||
map[string]interface{}{"SiteName": es.srv.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["Title"] = T("api.templates.password_change_body.title")
|
||||
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")
|
||||
|
||||
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 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)
|
||||
|
||||
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["Title"] = T("api.templates.user_access_token_body.title")
|
||||
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")
|
||||
|
||||
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 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)
|
||||
|
||||
link := fmt.Sprintf("%s/reset_password_complete?token=%s", siteURL, url.QueryEscape(token.Token))
|
||||
|
||||
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["Title"] = T("api.templates.reset_body.title")
|
||||
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["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 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)
|
||||
|
||||
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
|
||||
|
||||
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")
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) {
|
||||
if a.Srv().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))
|
||||
func (es *EmailService) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) {
|
||||
if es.EmailRateLimiter == nil {
|
||||
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
|
||||
}
|
||||
rateLimited, result, err := a.Srv().EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
rateLimited, result, err := es.EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
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
|
||||
}
|
||||
|
||||
if rateLimited {
|
||||
a.Log().Error("Invite emails rate limited.",
|
||||
es.srv.Log.Error("Invite emails rate limited.",
|
||||
mlog.String("user_id", senderUserId),
|
||||
mlog.String("team_id", team.Id),
|
||||
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",
|
||||
map[string]interface{}{"SenderName": senderName,
|
||||
"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["Title"] = utils.T("api.templates.invite_body.title")
|
||||
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
|
||||
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))
|
||||
continue
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) sendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, invites []string, siteURL string, message string) {
|
||||
if a.Srv().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))
|
||||
func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string) {
|
||||
if es.EmailRateLimiter == nil {
|
||||
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
|
||||
}
|
||||
rateLimited, result, err := a.Srv().EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
rateLimited, result, err := es.EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
a.Log().Error("Invite emails rate limited.",
|
||||
es.srv.Log.Error("Invite emails rate limited.",
|
||||
mlog.String("user_id", senderUserId),
|
||||
mlog.String("team_id", team.Id),
|
||||
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",
|
||||
map[string]interface{}{"SenderName": senderName,
|
||||
"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["Title"] = utils.T("api.templates.invite_body.title")
|
||||
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
|
||||
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))
|
||||
continue
|
||||
}
|
||||
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)))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
t := utils.NewHTMLTemplate(s.HTMLTemplates(), name)
|
||||
func (es *EmailService) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
t := utils.NewHTMLTemplate(es.srv.HTMLTemplates(), name)
|
||||
|
||||
var localT i18n.TranslateFunc
|
||||
if locale != "" {
|
||||
@@ -475,8 +480,8 @@ func (s *Server) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
|
||||
t.Props["Footer"] = localT("api.templates.email_footer")
|
||||
|
||||
if *s.Config().EmailSettings.FeedbackOrganization != "" {
|
||||
t.Props["Organization"] = localT("api.templates.email_organization") + *s.Config().EmailSettings.FeedbackOrganization
|
||||
if *es.srv.Config().EmailSettings.FeedbackOrganization != "" {
|
||||
t.Props["Organization"] = localT("api.templates.email_organization") + *es.srv.Config().EmailSettings.FeedbackOrganization
|
||||
} else {
|
||||
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["EmailInfo2"] = localT("api.templates.email_info2")
|
||||
t.Props["EmailInfo3"] = localT("api.templates.email_info3",
|
||||
map[string]interface{}{"SiteName": s.Config().TeamSettings.SiteName})
|
||||
t.Props["SupportEmail"] = *s.Config().SupportSettings.SupportEmail
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
t.Props["SupportEmail"] = *es.srv.Config().SupportSettings.SupportEmail
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (a *App) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
return a.Srv().newEmailTemplate(name, locale)
|
||||
}
|
||||
|
||||
func (a *App) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError {
|
||||
func (es *EmailService) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError {
|
||||
T := utils.GetUserTranslations(locale)
|
||||
|
||||
serverURL := condenseSiteURL(siteURL)
|
||||
|
||||
subject := T("api.templates.deactivate_subject",
|
||||
map[string]interface{}{"SiteName": a.ClientConfig()["SiteName"],
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"ServerURL": serverURL})
|
||||
|
||||
bodyPage := a.newEmailTemplate("deactivate_body", locale)
|
||||
bodyPage := es.newEmailTemplate("deactivate_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": serverURL})
|
||||
bodyPage.Props["Info"] = T("api.templates.deactivate_body.info",
|
||||
map[string]interface{}{"SiteURL": siteURL})
|
||||
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 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)
|
||||
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["Title"] = T("api.templates.remove_expired_license.body.title")
|
||||
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")
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (s *Server) sendNotificationMail(to, subject, htmlBody string) *model.AppError {
|
||||
if !*s.Config().EmailSettings.SendEmailNotifications {
|
||||
func (es *EmailService) sendNotificationMail(to, subject, htmlBody string) *model.AppError {
|
||||
if !*es.srv.Config().EmailSettings.SendEmailNotifications {
|
||||
return nil
|
||||
}
|
||||
return s.sendMail(to, subject, htmlBody)
|
||||
return es.sendMail(to, subject, htmlBody)
|
||||
}
|
||||
|
||||
func (a *App) sendNotificationMail(to, subject, htmlBody string) *model.AppError {
|
||||
return a.Srv().sendNotificationMail(to, subject, htmlBody)
|
||||
func (es *EmailService) sendMail(to, subject, htmlBody string) *model.AppError {
|
||||
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 {
|
||||
license := s.License()
|
||||
return mailservice.SendMailUsingConfig(to, subject, htmlBody, s.Config(), license != nil && *license.Features.Compliance)
|
||||
}
|
||||
|
||||
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()
|
||||
func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) *model.AppError {
|
||||
license := es.srv.License()
|
||||
config := es.srv.Config()
|
||||
|
||||
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 {
|
||||
return a.Srv().sendMailWithEmbeddedFiles(to, subject, htmlBody, embeddedFiles)
|
||||
func (es *EmailService) 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 := 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"
|
||||
)
|
||||
|
||||
func (s *Server) InitEmailBatching() {
|
||||
if *s.Config().EmailSettings.EnableEmailBatching {
|
||||
if s.EmailBatching == nil {
|
||||
s.EmailBatching = NewEmailBatchingJob(s, *s.Config().EmailSettings.EmailBatchingBufferSize)
|
||||
func (es *EmailService) InitEmailBatching() {
|
||||
if *es.srv.Config().EmailSettings.EnableEmailBatching {
|
||||
if es.EmailBatching == nil {
|
||||
es.EmailBatching = NewEmailBatchingJob(es, *es.srv.Config().EmailSettings.EmailBatchingBufferSize)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if !*a.Config().EmailSettings.EnableEmailBatching {
|
||||
func (es *EmailService) AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError {
|
||||
if !*es.srv.Config().EmailSettings.EnableEmailBatching {
|
||||
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.")
|
||||
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
|
||||
}
|
||||
|
||||
func NewEmailBatchingJob(s *Server, bufferSize int) *EmailBatchingJob {
|
||||
func NewEmailBatchingJob(es *EmailService, bufferSize int) *EmailBatchingJob {
|
||||
return &EmailBatchingJob{
|
||||
server: s,
|
||||
server: es.srv,
|
||||
newNotifications: make(chan *batchedNotification, bufferSize),
|
||||
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
|
||||
// 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)))
|
||||
}
|
||||
@@ -194,68 +194,68 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) sendBatchedEmailNotification(userId string, notifications []*batchedNotification) {
|
||||
user, err := s.Store.User().Get(userId)
|
||||
func (es *EmailService) sendBatchedEmailNotification(userId string, notifications []*batchedNotification) {
|
||||
user, err := es.srv.Store.User().Get(userId)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to find recipient for batched email notification")
|
||||
return
|
||||
}
|
||||
|
||||
translateFunc := utils.GetUserTranslations(user.Locale)
|
||||
displayNameFormat := *s.Config().TeamSettings.TeammateNameDisplay
|
||||
displayNameFormat := *es.srv.Config().TeamSettings.TeammateNameDisplay
|
||||
|
||||
var contents string
|
||||
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 {
|
||||
mlog.Warn("Unable to find sender of post for batched email notification")
|
||||
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 {
|
||||
mlog.Warn("Unable to find channel of post for batched email notification")
|
||||
continue
|
||||
}
|
||||
|
||||
emailNotificationContentsType := model.EMAIL_NOTIFICATION_CONTENTS_FULL
|
||||
if license := s.License(); license != nil && *license.Features.EmailNotificationContents {
|
||||
emailNotificationContentsType = *s.Config().EmailSettings.EmailNotificationContentsType
|
||||
if license := es.srv.License(); license != nil && *license.Features.EmailNotificationContents {
|
||||
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)
|
||||
|
||||
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(),
|
||||
"Month": translateFunc(tm.Month().String()),
|
||||
"Day": tm.Day(),
|
||||
})
|
||||
|
||||
body := s.newEmailTemplate("post_batched_body", user.Locale)
|
||||
body.Props["SiteURL"] = *s.Config().ServiceSettings.SiteURL
|
||||
body := es.newEmailTemplate("post_batched_body", user.Locale)
|
||||
body.Props["SiteURL"] = *es.srv.Config().ServiceSettings.SiteURL
|
||||
body.Props["Posts"] = template.HTML(contents)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
var template *utils.HTMLTemplate
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
template = s.newEmailTemplate("post_batched_post_full", userLocale)
|
||||
template = es.newEmailTemplate("post_batched_post_full", userLocale)
|
||||
} 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["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["SenderName"] = sender.GetDisplayName(displayNameFormat)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestHandleNewNotifications(t *testing.T) {
|
||||
id3 := model.NewId()
|
||||
|
||||
// test queueing of received posts by user
|
||||
job := NewEmailBatchingJob(th.Server, 128)
|
||||
job := NewEmailBatchingJob(th.Server.EmailService, 128)
|
||||
|
||||
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")
|
||||
|
||||
// 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: "test2"}, &model.Team{Name: "team"})
|
||||
@@ -77,7 +77,7 @@ func TestCheckPendingNotifications(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
job := NewEmailBatchingJob(th.Server, 128)
|
||||
job := NewEmailBatchingJob(th.Server.EmailService, 128)
|
||||
job.pendingNotifications[th.BasicUser.Id] = []*batchedNotification{
|
||||
{
|
||||
post: &model.Post{
|
||||
@@ -205,7 +205,7 @@ func TestCheckPendingNotificationsDefaultInterval(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
job := NewEmailBatchingJob(th.Server, 128)
|
||||
job := NewEmailBatchingJob(th.Server.EmailService, 128)
|
||||
|
||||
// bypasses recent user activity check
|
||||
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()
|
||||
defer th.TearDown()
|
||||
|
||||
job := NewEmailBatchingJob(th.Server, 128)
|
||||
job := NewEmailBatchingJob(th.Server.EmailService, 128)
|
||||
|
||||
// bypasses recent user activity check
|
||||
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
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
@@ -329,6 +329,6 @@ func TestRenderBatchedPostFull(t *testing.T) {
|
||||
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.")
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
})
|
||||
@@ -153,7 +153,7 @@ func (a *App) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (
|
||||
T := utils.GetUserTranslations(user.Locale)
|
||||
|
||||
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())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -57,7 +57,7 @@ func (a *App) sendNotificationEmail(notification *PostNotification, user *model.
|
||||
}
|
||||
|
||||
if sendBatched {
|
||||
if err := a.AddNotificationEmailToBatch(user, post, team); err == nil {
|
||||
if err := a.Srv().EmailService.AddNotificationEmailToBatch(user, post, team); err == 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)
|
||||
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
var bodyPage *utils.HTMLTemplate
|
||||
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 = html.EscapeString(postMessage)
|
||||
normalizedPostMessage := a.generateHyperlinkForChannels(postMessage, teamName, landingURL)
|
||||
bodyPage.Props["PostMessage"] = template.HTML(normalizedPostMessage)
|
||||
} else {
|
||||
bodyPage = a.newEmailTemplate("post_body_generic", recipient.Locale)
|
||||
bodyPage = a.Srv().EmailService.newEmailTemplate("post_body_generic", recipient.Locale)
|
||||
}
|
||||
|
||||
bodyPage.Props["SiteURL"] = a.GetSiteURL()
|
||||
|
||||
@@ -584,7 +584,7 @@ func (a *App) CompleteSwitchWithOAuth(service string, userData io.Reader, email
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -853,7 +853,7 @@ func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *
|
||||
T := utils.GetUserTranslations(user.Locale)
|
||||
|
||||
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))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -170,28 +170,6 @@ func (a *OpenTracingAppLayer) AddDirectChannels(teamId string, user *model.User)
|
||||
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 {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddPublicKey")
|
||||
@@ -2245,28 +2223,6 @@ func (a *OpenTracingAppLayer) CreateUserWithToken(user *model.User, token *model
|
||||
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) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateWebhookPost")
|
||||
@@ -12472,28 +12428,6 @@ func (a *OpenTracingAppLayer) SendAutoResponseIfNecessary(channel *model.Channel
|
||||
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 {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendEmailVerification")
|
||||
@@ -12533,21 +12467,6 @@ func (a *OpenTracingAppLayer) SendEphemeralPost(userId string, post *model.Post)
|
||||
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) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendNotifications")
|
||||
@@ -12592,72 +12511,6 @@ func (a *OpenTracingAppLayer) SendPasswordReset(email string, siteURL string) (b
|
||||
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) {
|
||||
origCtx := a.ctx
|
||||
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 api.app.sendNotificationMail(to, subject, htmlBody)
|
||||
return api.app.Srv().EmailService.sendNotificationMail(to, subject, htmlBody)
|
||||
}
|
||||
|
||||
// Plugin Section
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/rs/cors"
|
||||
rudder "github.com/rudderlabs/analytics-go"
|
||||
|
||||
"github.com/throttled/throttled"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/audit"
|
||||
@@ -89,8 +88,7 @@ type Server struct {
|
||||
PluginConfigListenerId string
|
||||
PluginsLock sync.RWMutex
|
||||
|
||||
EmailBatching *EmailBatchingJob
|
||||
EmailRateLimiter *throttled.GCRARateLimiter
|
||||
EmailService *EmailService
|
||||
|
||||
hubs []*Hub
|
||||
hashSeed maphash.Seed
|
||||
@@ -312,10 +310,6 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
})
|
||||
|
||||
if err := s.setupInviteEmailRateLimiting(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.initEnterprise()
|
||||
|
||||
if s.newStore == nil {
|
||||
@@ -356,6 +350,12 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
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" {
|
||||
s.LoadLicense()
|
||||
}
|
||||
@@ -366,19 +366,19 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
if err := s.ensurePostActionCookieSecret(); err != nil {
|
||||
if err = s.ensurePostActionCookieSecret(); err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
if err := s.ensureFirstServerRunTimestamp(); err != nil {
|
||||
if err = s.ensureFirstServerRunTimestamp(); err != nil {
|
||||
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),
|
||||
}
|
||||
|
||||
if err := mailservice.TestConnection(s.Config()); err != nil {
|
||||
mlog.Error("Mail server connection test is failed: " + err.Message)
|
||||
if appErr := mailservice.TestConnection(s.Config()); appErr != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -437,9 +437,8 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
s.timezones = timezones.New()
|
||||
// Start email batching because it's not like the other jobs
|
||||
s.InitEmailBatching()
|
||||
s.AddConfigListener(func(_, _ *model.Config) {
|
||||
s.InitEmailBatching()
|
||||
s.EmailService.InitEmailBatching()
|
||||
})
|
||||
|
||||
// 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 })
|
||||
}
|
||||
|
||||
if err := s.Store.Status().ResetAll(); err != nil {
|
||||
mlog.Error("Error to reset the server status.", mlog.Err(err))
|
||||
if appErr = s.Store.Status().ResetAll(); appErr != nil {
|
||||
mlog.Error("Error to reset the server status.", mlog.Err(appErr))
|
||||
}
|
||||
|
||||
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))
|
||||
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))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -376,7 +376,7 @@ func (a *App) CreateUserAccessToken(token *model.UserAccessToken) (*model.UserAc
|
||||
|
||||
// Don't send emails to bot users.
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
19
app/team.go
19
app/team.go
@@ -1148,7 +1148,7 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderI
|
||||
|
||||
if len(goodEmails) > 0 {
|
||||
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
|
||||
@@ -1231,7 +1231,11 @@ func (a *App) InviteGuestsToChannelsGracefully(teamId string, guestsInvite *mode
|
||||
|
||||
if len(goodEmails) > 0 {
|
||||
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
|
||||
@@ -1268,7 +1272,7 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1292,12 +1296,15 @@ func (a *App) InviteGuestsToChannels(teamId string, guestsInvite *model.GuestsIn
|
||||
|
||||
if len(invalidEmailList) > 0 {
|
||||
s := strings.Join(invalidEmailList, ", ")
|
||||
err := model.NewAppError("InviteGuestsToChannels", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest)
|
||||
return err
|
||||
return model.NewAppError("InviteGuestsToChannels", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
47
app/user.go
47
app/user.go
@@ -139,7 +139,7 @@ func (a *App) CreateUserWithInviteId(user *model.User, inviteId string) (*model.
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func (a *App) CreateUserAsAdmin(user *model.User) (*model.User, *model.AppError)
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ func (a *App) CreateUserFromSignup(user *model.User) (*model.User, *model.AppErr
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -1165,7 +1165,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
||||
})
|
||||
} else {
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -1174,7 +1174,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
||||
|
||||
if userUpdate.New.Username != userUpdate.Old.Username {
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -1233,7 +1233,7 @@ func (a *App) UpdateMfa(activate bool, userId, token string) *model.AppError {
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -1272,7 +1272,7 @@ func (a *App) UpdatePasswordSendEmail(user *model.User, newPassword, method stri
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -1340,7 +1340,7 @@ func (a *App) SendPasswordReset(email string, siteURL string) (bool, *model.AppE
|
||||
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) {
|
||||
@@ -1558,15 +1558,15 @@ func (a *App) PermanentDeleteAllUsers() *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 {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -1600,7 +1600,7 @@ func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppErr
|
||||
|
||||
if user.Email != tokenData.Email {
|
||||
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))
|
||||
}
|
||||
})
|
||||
@@ -1613,29 +1613,6 @@ func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppErr
|
||||
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) {
|
||||
rtoken, err := a.Srv().Store.Token().GetByToken(token)
|
||||
if err != nil {
|
||||
|
||||
@@ -381,7 +381,7 @@ func TestUpdateUserEmail(t *testing.T) {
|
||||
assert.Equal(t, currentEmail, user2.Email)
|
||||
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)
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
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.")
|
||||
|
||||
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.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())
|
||||
}
|
||||
})
|
||||
|
||||
Ссылка в новой задаче
Block a user