diff --git a/backend/internal/service/email_service.go b/backend/internal/service/email_service.go index 7859dc1..85659a1 100644 --- a/backend/internal/service/email_service.go +++ b/backend/internal/service/email_service.go @@ -4,10 +4,6 @@ import ( "bytes" "crypto/tls" "fmt" - "github.com/stonith404/pocket-id/backend/internal/common" - "github.com/stonith404/pocket-id/backend/internal/model" - "github.com/stonith404/pocket-id/backend/internal/utils/email" - "gorm.io/gorm" htemplate "html/template" "mime/multipart" "mime/quotedprintable" @@ -17,6 +13,11 @@ import ( "os" ttemplate "text/template" "time" + + "github.com/stonith404/pocket-id/backend/internal/common" + "github.com/stonith404/pocket-id/backend/internal/model" + "github.com/stonith404/pocket-id/backend/internal/utils/email" + "gorm.io/gorm" ) var netDialer = &net.Dialer{ @@ -89,16 +90,31 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T ) c.Body(body) - // Set up the TLS configuration + // Connect to the SMTP server + client, err := srv.getSmtpClient() + if err != nil { + return fmt.Errorf("failed to connect to SMTP server: %w", err) + } + defer client.Close() + + // Send the email + if err := srv.sendEmailContent(client, toEmail, c); err != nil { + return fmt.Errorf("send email content: %w", err) + } + + return nil +} + +func (srv *EmailService) getSmtpClient() (client *smtp.Client, err error) { + port := srv.appConfigService.DbConfig.SmtpPort.Value + smtpAddress := srv.appConfigService.DbConfig.SmtpHost.Value + ":" + port + tlsConfig := &tls.Config{ InsecureSkipVerify: srv.appConfigService.DbConfig.SmtpSkipCertVerify.Value == "true", ServerName: srv.appConfigService.DbConfig.SmtpHost.Value, } // Connect to the SMTP server - port := srv.appConfigService.DbConfig.SmtpPort.Value - smtpAddress := srv.appConfigService.DbConfig.SmtpHost.Value + ":" + port - var client *smtp.Client if srv.appConfigService.DbConfig.SmtpTls.Value == "false" { client, err = srv.connectToSmtpServer(smtpAddress) } else if port == "465" { @@ -112,24 +128,14 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T tlsConfig, ) } - if err != nil { - return fmt.Errorf("failed to connect to SMTP server: %w", err) - } - defer client.Close() - - // Set the hello message manually as for example Google rejects the default "localhost" value - hostname, err := os.Hostname() - if err == nil { - if err := client.Hello(hostname); err != nil { - return fmt.Errorf("failed to say hello to SMTP server: %w", err) - } + return nil, fmt.Errorf("failed to connect to SMTP server: %w", err) } + // Set up the authentication if user or password are set smtpUser := srv.appConfigService.DbConfig.SmtpUser.Value smtpPassword := srv.appConfigService.DbConfig.SmtpPassword.Value - // Set up the authentication if user or password are set if smtpUser != "" || smtpPassword != "" { auth := smtp.PlainAuth("", srv.appConfigService.DbConfig.SmtpUser.Value, @@ -137,16 +143,11 @@ func SendEmail[V any](srv *EmailService, toEmail email.Address, template email.T srv.appConfigService.DbConfig.SmtpHost.Value, ) if err := client.Auth(auth); err != nil { - return fmt.Errorf("failed to authenticate SMTP client: %w", err) + return nil, fmt.Errorf("failed to authenticate SMTP client: %w", err) } } - // Send the email - if err := srv.sendEmailContent(client, toEmail, c); err != nil { - return fmt.Errorf("send email content: %w", err) - } - - return nil + return client, err } func (srv *EmailService) connectToSmtpServer(serverAddr string) (*smtp.Client, error) { @@ -155,6 +156,15 @@ func (srv *EmailService) connectToSmtpServer(serverAddr string) (*smtp.Client, e return nil, fmt.Errorf("failed to connect to SMTP server: %w", err) } client, err := smtp.NewClient(conn, srv.appConfigService.DbConfig.SmtpHost.Value) + if err != nil { + conn.Close() + return nil, fmt.Errorf("failed to create SMTP client: %w", err) + } + + if err := srv.sendHelloCommand(client); err != nil { + return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err) + } + return client, err } @@ -174,6 +184,10 @@ func (srv *EmailService) connectToSmtpServerUsingImplicitTLS(serverAddr string, return nil, fmt.Errorf("failed to create SMTP client: %w", err) } + if err := srv.sendHelloCommand(client); err != nil { + return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err) + } + return client, nil } @@ -189,12 +203,26 @@ func (srv *EmailService) connectToSmtpServerUsingStartTLS(serverAddr string, tls return nil, fmt.Errorf("failed to create SMTP client: %w", err) } + if err := srv.sendHelloCommand(client); err != nil { + return nil, fmt.Errorf("failed to say hello to SMTP server: %w", err) + } + if err := client.StartTLS(tlsConfig); err != nil { return nil, fmt.Errorf("failed to start TLS: %w", err) } return client, nil } +func (srv *EmailService) sendHelloCommand(client *smtp.Client) error { + hostname, err := os.Hostname() + if err == nil { + if err := client.Hello(hostname); err != nil { + return err + } + } + return nil +} + func (srv *EmailService) sendEmailContent(client *smtp.Client, toEmail email.Address, c *email.Composer) error { if err := client.Mail(srv.appConfigService.DbConfig.SmtpFrom.Value); err != nil { return fmt.Errorf("failed to set sender: %w", err) diff --git a/frontend/src/routes/settings/admin/application-configuration/forms/app-config-email-form.svelte b/frontend/src/routes/settings/admin/application-configuration/forms/app-config-email-form.svelte index fb65b4d..30cf845 100644 --- a/frontend/src/routes/settings/admin/application-configuration/forms/app-config-email-form.svelte +++ b/frontend/src/routes/settings/admin/application-configuration/forms/app-config-email-form.svelte @@ -77,7 +77,7 @@ isSendingTestEmail = true; await appConfigService .sendTestEmail() - .then(() => toast.success('Test email sent successfully to your Email address.')) + .then(() => toast.success('Test email sent successfully to your email address.')) .catch(() => toast.error('Failed to send test email. Check the server logs for more information.') )