package mails import ( "html/template" "net/mail" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/mails/templates" "github.com/pocketbase/pocketbase/tools/mailer" ) // SendRecordAuthAlert sends a new device login alert to the specified auth record. func SendRecordAuthAlert(app core.App, authRecord *core.Record) error { mailClient := app.NewMailClient() subject, body, err := resolveEmailTemplate(app, authRecord, authRecord.Collection().AuthAlert.EmailTemplate, nil) if err != nil { return err } message := &mailer.Message{ From: mail.Address{ Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } event := new(core.MailerRecordEvent) event.App = app event.Mailer = mailClient event.Message = message event.Record = authRecord return app.OnMailerRecordAuthAlertSend().Trigger(event, func(e *core.MailerRecordEvent) error { return e.Mailer.Send(e.Message) }) } // SendRecordOTP sends OTP email to the specified auth record. func SendRecordOTP(app core.App, authRecord *core.Record, otpId string, pass string) error { mailClient := app.NewMailClient() subject, body, err := resolveEmailTemplate(app, authRecord, authRecord.Collection().OTP.EmailTemplate, map[string]any{ core.EmailPlaceholderOTPId: otpId, core.EmailPlaceholderOTP: pass, }) if err != nil { return err } message := &mailer.Message{ From: mail.Address{ Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } event := new(core.MailerRecordEvent) event.App = app event.Mailer = mailClient event.Message = message event.Record = authRecord event.Meta = map[string]any{ "otpId": otpId, "password": pass, } return app.OnMailerRecordOTPSend().Trigger(event, func(e *core.MailerRecordEvent) error { return e.Mailer.Send(e.Message) }) } // SendRecordPasswordReset sends a password reset request email to the specified auth record. func SendRecordPasswordReset(app core.App, authRecord *core.Record) error { token, tokenErr := authRecord.NewPasswordResetToken() if tokenErr != nil { return tokenErr } mailClient := app.NewMailClient() subject, body, err := resolveEmailTemplate(app, authRecord, authRecord.Collection().ResetPasswordTemplate, map[string]any{ core.EmailPlaceholderToken: token, }) if err != nil { return err } message := &mailer.Message{ From: mail.Address{ Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } event := new(core.MailerRecordEvent) event.App = app event.Mailer = mailClient event.Message = message event.Record = authRecord event.Meta = map[string]any{"token": token} return app.OnMailerRecordPasswordResetSend().Trigger(event, func(e *core.MailerRecordEvent) error { return e.Mailer.Send(e.Message) }) } // SendRecordVerification sends a verification request email to the specified auth record. func SendRecordVerification(app core.App, authRecord *core.Record) error { token, tokenErr := authRecord.NewVerificationToken() if tokenErr != nil { return tokenErr } mailClient := app.NewMailClient() subject, body, err := resolveEmailTemplate(app, authRecord, authRecord.Collection().VerificationTemplate, map[string]any{ core.EmailPlaceholderToken: token, }) if err != nil { return err } message := &mailer.Message{ From: mail.Address{ Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, To: []mail.Address{{Address: authRecord.Email()}}, Subject: subject, HTML: body, } event := new(core.MailerRecordEvent) event.App = app event.Mailer = mailClient event.Message = message event.Record = authRecord event.Meta = map[string]any{"token": token} return app.OnMailerRecordVerificationSend().Trigger(event, func(e *core.MailerRecordEvent) error { return e.Mailer.Send(e.Message) }) } // SendRecordChangeEmail sends a change email confirmation email to the specified auth record. func SendRecordChangeEmail(app core.App, authRecord *core.Record, newEmail string) error { token, tokenErr := authRecord.NewEmailChangeToken(newEmail) if tokenErr != nil { return tokenErr } mailClient := app.NewMailClient() subject, body, err := resolveEmailTemplate(app, authRecord, authRecord.Collection().ConfirmEmailChangeTemplate, map[string]any{ core.EmailPlaceholderToken: token, }) if err != nil { return err } message := &mailer.Message{ From: mail.Address{ Name: app.Settings().Meta.SenderName, Address: app.Settings().Meta.SenderAddress, }, To: []mail.Address{{Address: newEmail}}, Subject: subject, HTML: body, } event := new(core.MailerRecordEvent) event.App = app event.Mailer = mailClient event.Message = message event.Record = authRecord event.Meta = map[string]any{ "token": token, "newEmail": newEmail, } return app.OnMailerRecordEmailChangeSend().Trigger(event, func(e *core.MailerRecordEvent) error { return e.Mailer.Send(e.Message) }) } func resolveEmailTemplate( app core.App, authRecord *core.Record, emailTemplate core.EmailTemplate, placeholders map[string]any, ) (subject string, body string, err error) { if placeholders == nil { placeholders = map[string]any{} } // register default system placeholders if _, ok := placeholders[core.EmailPlaceholderAppName]; !ok { placeholders[core.EmailPlaceholderAppName] = app.Settings().Meta.AppName } if _, ok := placeholders[core.EmailPlaceholderAppURL]; !ok { placeholders[core.EmailPlaceholderAppURL] = app.Settings().Meta.AppURL } // register default auth record placeholders for _, field := range authRecord.Collection().Fields { if field.GetHidden() { continue } fieldPlacehodler := "{RECORD:" + field.GetName() + "}" if _, ok := placeholders[fieldPlacehodler]; !ok { placeholders[fieldPlacehodler] = authRecord.Get(field.GetName()) } } subject, rawBody := emailTemplate.Resolve(placeholders) params := struct { HTMLContent template.HTML }{ HTMLContent: template.HTML(rawBody), } body, err = resolveTemplateContent(params, templates.Layout, templates.HTMLBody) if err != nil { return "", "", err } return subject, body, nil }