Permalink
Browse files

Make SMTP configuration global.

We're going to send all emails ourselves directly.

Signed-off-by: David Calavera <david.calavera@gmail.com>
  • Loading branch information...
calavera committed Aug 31, 2017
1 parent ea5cb58 commit 2f48aa8a7b8a38e83e60f88e11a7acbb7341ef41
Showing with 59 additions and 52 deletions.
  1. +11 −8 README.md
  2. +3 −3 api/api.go
  3. +1 −1 api/middleware.go
  4. +1 −2 api/recover.go
  5. +4 −5 app.json
  6. +1 −1 cmd/serve_cmd.go
  7. +19 −14 conf/configuration.go
  8. +5 −6 example.env
  9. +14 −12 mailer/mailer.go
View
@@ -144,32 +144,35 @@ Sending email is not required, but highly recommended for password recovery.
If enabled, you must provide the required values below.
```
GOTRUE_MAILER_ADMIN_EMAIL=support@example.com
GOTRUE_MAILER_PORT=25
GOTRUE_SMTP_HOST=smtp.mandrillapp.com
GOTRUE_SMTP_PORT=587
GOTRUE_SMTP_USER=smtp-delivery@example.com
GOTRUE_SMTP_PASS=correcthorsebatterystaple
GOTRUE_SMTP_ADMIN_EMAIL=support@example.com
GOTRUE_MAILER_SUBJECTS_CONFIRMATION="Please confirm"
```
`MAILER_ADMIN_EMAIL` - `string` **required**
`SMTP_ADMIN_EMAIL` - `string` **required**
The `From` email address for all emails sent.
`MAILER_HOST` - `string` **required**
`SMTP_HOST` - `string` **required**
The mail server hostname to send emails through.
`MAILER_PORT` - `number` **required**
`SMTP_PORT` - `number` **required**
The port number to connect to the mail server on.
`MAILER_USER` - `string`
`SMTP_USER` - `string`
If the mail server requires authentication, the username to use.
`MAILER_PASS` - `string`
`SMTP_PASS` - `string`
If the mail server requires authentication, the password to use.
`MAILER_MAX_FREQUENCY` - `number`
`SMTP_MAX_FREQUENCY` - `number`
Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. The value is the number of seconds. Defaults to 900 (15 minutes).
View
@@ -151,7 +151,7 @@ func NewAPIFromConfigFile(filename string, version string) (*API, *conf.Configur
return nil, nil, err
}
ctx, err := WithInstanceConfig(context.Background(), config, "")
ctx, err := WithInstanceConfig(context.Background(), globalConfig.SMTP, config, "")
if err != nil {
logrus.Fatalf("Error loading instance config: %+v", err)
}
@@ -167,10 +167,10 @@ func (a *API) HealthCheck(w http.ResponseWriter, r *http.Request) error {
})
}
func WithInstanceConfig(ctx context.Context, config *conf.Configuration, instanceID string) (context.Context, error) {
func WithInstanceConfig(ctx context.Context, smtp conf.SMTPConfiguration, config *conf.Configuration, instanceID string) (context.Context, error) {
ctx = withConfig(ctx, config)
mailer := mailer.NewMailer(config)
mailer := mailer.NewMailer(smtp, config)
ctx = withMailer(ctx, mailer)
ctx = withInstanceID(ctx, instanceID)
View
@@ -94,7 +94,7 @@ func (a *API) loadInstanceConfig(w http.ResponseWriter, r *http.Request) (contex
logEntrySetField(r, "site_url", config.SiteURL)
ctx = withNetlifyID(ctx, claims.NetlifyID)
ctx, err = WithInstanceConfig(ctx, config, instanceID)
ctx, err = WithInstanceConfig(ctx, a.config.SMTP, config, instanceID)
if err != nil {
return nil, internalServerError("Error loading instance config").WithInternalError(err)
}
View
@@ -16,7 +16,6 @@ type RecoverParams struct {
// Recover sends a recovery email
func (a *API) Recover(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
config := a.getConfig(ctx)
instanceID := getInstanceID(ctx)
params := &RecoverParams{}
jsonDecoder := json.NewDecoder(r.Body)
@@ -38,7 +37,7 @@ func (a *API) Recover(w http.ResponseWriter, r *http.Request) error {
return internalServerError("Database error finding user").WithInternalError(err)
}
if user.RecoverySentAt == nil || user.RecoverySentAt.Add(config.Mailer.MaxFrequency).Before(time.Now()) {
if user.RecoverySentAt == nil || user.RecoverySentAt.Add(a.config.SMTP.MaxFrequency).Before(time.Now()) {
user.GenerateRecoveryToken()
if err := a.db.UpdateUser(user); err != nil {
return internalServerError("Database error updating user").WithInternalError(err)
View
@@ -17,11 +17,10 @@
"GOTRUE_JWT_SECRET": {
"required": true
},
"GOTRUE_MAILER_ADMIN_EMAIL": {},
"GOTRUE_MAILER_HOST": {},
"GOTRUE_MAILER_MEMBER_FOLDER": {},
"GOTRUE_MAILER_PASS": {},
"GOTRUE_MAILER_PORT": {},
"GOTRUE_SMTP_ADMIN_EMAIL": {},
"GOTRUE_SMTP_HOST": {},
"GOTRUE_SMTP_PASS": {},
"GOTRUE_SMTP_PORT": {},
"GOTRUE_MAILER_SITE_URL": {},
"GOTRUE_MAILER_SUBJECTS_CONFIRMATION": {},
"GOTRUE_MAILER_SUBJECTS_RECOVERY": {},
View
@@ -26,7 +26,7 @@ func serve(globalConfig *conf.GlobalConfiguration, config *conf.Configuration) {
}
defer db.Close()
ctx, err := api.WithInstanceConfig(context.Background(), config, "")
ctx, err := api.WithInstanceConfig(context.Background(), globalConfig.SMTP, config, "")
if err != nil {
logrus.Fatalf("Error loading instance config: %+v", err)
}
View
@@ -47,6 +47,7 @@ type GlobalConfiguration struct {
Logging nconf.LoggingConfig `envconfig:"LOG"`
OperatorToken string `split_words:"true" required:"true"`
MultiInstanceMode bool
SMTP SMTPConfiguration
}
// EmailContentConfiguration holds the configuration for emails, both subjects and template URLs.
@@ -65,21 +66,24 @@ type ExternalProviderConfiguration struct {
RedirectURL string `json:"redirect_url"`
}
type SMTPConfiguration struct {
MaxFrequency time.Duration `json:"max_frequency" split_words:"true"`
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Pass string `json:"pass"`
AdminEmail string `json:"admin_email" split_words:"true"`
}
// Configuration holds all the per-instance configuration.
type Configuration struct {
SiteURL string `json:"site_url" split_words:"true" required:"true"`
JWT JWTConfiguration `json:"jwt"`
Mailer struct {
MaxFrequency time.Duration `json:"max_frequency" split_words:"true"`
Autoconfirm bool `json:"autoconfirm"`
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Pass string `json:"pass"`
AdminEmail string `json:"admin_email" split_words:"true"`
Subjects EmailContentConfiguration `json:"subjects"`
Templates EmailContentConfiguration `json:"templates"`
URLPaths EmailContentConfiguration `json:"url_paths"`
Autoconfirm bool `json:"autoconfirm"`
Subjects EmailContentConfiguration `json:"subjects"`
Templates EmailContentConfiguration `json:"templates"`
URLPaths EmailContentConfiguration `json:"url_paths"`
} `json:"mailer"`
External ExternalProviderConfiguration `json:"external"`
}
@@ -108,9 +112,14 @@ func LoadGlobal(filename string) (*GlobalConfiguration, error) {
if err := envconfig.Process("gotrue", config); err != nil {
return nil, err
}
if _, err := nconf.ConfigureLogging(&config.Logging); err != nil {
return nil, err
}
if config.SMTP.MaxFrequency == 0 {
config.SMTP.MaxFrequency = 15 * time.Minute
}
return config, nil
}
@@ -138,10 +147,6 @@ func (config *Configuration) ApplyDefaults() {
config.JWT.Exp = 3600
}
if config.Mailer.MaxFrequency == 0 {
config.Mailer.MaxFrequency = 15 * time.Minute
}
if config.Mailer.Templates.Invite == "" {
config.Mailer.Templates.Invite = "/.netlify/gotrue/templates/invite.html"
}
View
@@ -6,11 +6,10 @@ DATABASE_URL=gorm.db
GOTRUE_API_HOST=localhost
PORT=9999
GOTRUE_SITE_URL=https://my-jam-site.example.com
GOTRUE_MAILER_HOST=smtp.mandrillapp.com
GOTRUE_MAILER_PORT=587
GOTRUE_MAILER_USER=test@example.com
GOTRUE_MAILER_PASS=super-secret-password
GOTRUE_MAILER_MEMBER_FOLDER=/users
GOTRUE_MAILER_ADMIN_EMAIL=admin@example.com
GOTRUE_SMTP_HOST=smtp.mandrillapp.com
GOTRUE_SMTP_PORT=587
GOTRUE_SMTP_USER=test@example.com
GOTRUE_SMTP_PASS=super-secret-password
GOTRUE_SMTP_ADMIN_EMAIL=admin@example.com
GOTRUE_MAILER_SUBJECTS_CONFIRMATION="Welcome to GoTrue!"
GOTRUE_MAILER_SUBJECTS_RECOVERY="Reset your GoTrue password!"
View
@@ -46,6 +46,7 @@ type TemplateMailer struct {
SiteURL string
Config *conf.Configuration
TemplateMailer *mailme.Mailer
MaxFrequency time.Duration
}
type noopMailer struct {
@@ -58,21 +59,22 @@ type MailSubjects struct {
}
// NewMailer returns a new gotrue mailer
func NewMailer(conf *conf.Configuration) Mailer {
if conf.Mailer.Host == "" {
func NewMailer(smtp conf.SMTPConfiguration, instanceConfig *conf.Configuration) Mailer {
if smtp.Host == "" {
return &noopMailer{}
}
return &TemplateMailer{
SiteURL: conf.SiteURL,
Config: conf,
SiteURL: instanceConfig.SiteURL,
MaxFrequency: smtp.MaxFrequency,
Config: instanceConfig,
TemplateMailer: &mailme.Mailer{
Host: conf.Mailer.Host,
Port: conf.Mailer.Port,
User: conf.Mailer.User,
Pass: conf.Mailer.Pass,
From: conf.Mailer.AdminEmail,
BaseURL: conf.SiteURL,
Host: smtp.Host,
Port: smtp.Port,
User: smtp.User,
Pass: smtp.Pass,
From: smtp.AdminEmail,
BaseURL: instanceConfig.SiteURL,
},
}
}
@@ -93,7 +95,7 @@ func (m TemplateMailer) ValidateEmail(email string) error {
// InviteMail sends a invite mail to a new user
func (m *TemplateMailer) InviteMail(user *models.User) error {
if user.ConfirmationSentAt != nil && !user.ConfirmationSentAt.Add(m.Config.Mailer.MaxFrequency).Before(time.Now()) {
if user.ConfirmationSentAt != nil && !user.ConfirmationSentAt.Add(m.MaxFrequency).Before(time.Now()) {
return nil
}
@@ -120,7 +122,7 @@ func (m *TemplateMailer) InviteMail(user *models.User) error {
// ConfirmationMail sends a signup confirmation mail to a new user
func (m *TemplateMailer) ConfirmationMail(user *models.User) error {
if user.ConfirmationSentAt != nil && !user.ConfirmationSentAt.Add(m.Config.Mailer.MaxFrequency).Before(time.Now()) {
if user.ConfirmationSentAt != nil && !user.ConfirmationSentAt.Add(m.MaxFrequency).Before(time.Now()) {
return nil
}

0 comments on commit 2f48aa8

Please sign in to comment.