From 4aeeee425b438949b721e156eb442a4f5bc27114 Mon Sep 17 00:00:00 2001 From: onysd Date: Mon, 20 Jul 2026 09:42:26 +0300 Subject: [PATCH] fixes --- .env.example | 2 +- cmd/telesrv/main.go | 1 + internal/config/config.go | 2 +- internal/otpdelivery/smtp/sender.go | 25 ++++++++++++++++++---- internal/otpdelivery/smtp/sender_test.go | 27 ++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index b7e76fb4..1bff6ef4 100644 --- a/.env.example +++ b/.env.example @@ -67,7 +67,7 @@ TELESRV_SMTP_PORT=587 TELESRV_SMTP_USERNAME= TELESRV_SMTP_PASSWORD= TELESRV_SMTP_FROM= -TELESRV_SMTP_FROM_NAME=telesrv +TELESRV_SMTP_FROM_NAME=OwpenGram TELESRV_SMTP_TLS=starttls TELESRV_SMTP_TIMEOUT=10s diff --git a/cmd/telesrv/main.go b/cmd/telesrv/main.go index cc13aba3..fa23e834 100644 --- a/cmd/telesrv/main.go +++ b/cmd/telesrv/main.go @@ -573,6 +573,7 @@ func run(logger *zap.Logger) error { Password: cfg.SMTPPassword, From: cfg.SMTPFrom, FromName: cfg.SMTPFromName, + AppName: cfg.PublicAppName, TLSMode: cfg.SMTPTLSMode, Timeout: cfg.SMTPTimeout, }) diff --git a/internal/config/config.go b/internal/config/config.go index 7428a0ab..546bd18f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -525,7 +525,7 @@ func Load() (Config, error) { SMTPUsername: envOr("TELESRV_SMTP_USERNAME", ""), SMTPPassword: envOr("TELESRV_SMTP_PASSWORD", ""), SMTPFrom: envOr("TELESRV_SMTP_FROM", ""), - SMTPFromName: envOr("TELESRV_SMTP_FROM_NAME", "telesrv"), + SMTPFromName: envOr("TELESRV_SMTP_FROM_NAME", "OwpenGram"), SMTPTLSMode: strings.ToLower(strings.TrimSpace(envOr("TELESRV_SMTP_TLS", "starttls"))), SMTPTimeout: envDurationOr("TELESRV_SMTP_TIMEOUT", 10*time.Second), LangPackSeedDir: envOr("TELESRV_LANGPACK_SEED_DIR", "data/langpack"), diff --git a/internal/otpdelivery/smtp/sender.go b/internal/otpdelivery/smtp/sender.go index 207d0223..d3e07545 100644 --- a/internal/otpdelivery/smtp/sender.go +++ b/internal/otpdelivery/smtp/sender.go @@ -22,8 +22,14 @@ type Config struct { Password string From string FromName string - TLSMode string - Timeout time.Duration + // AppName is the product name shown in the email subject/body (e.g. + // "Your login code"). Defaults to "telesrv" if empty, matching + // this package's other defaults — callers should pass the same brand + // name used elsewhere (e.g. Config.PublicAppName), or codes will read as + // coming from "telesrv" regardless of the operator's own branding. + AppName string + TLSMode string + Timeout time.Duration } type Sender struct { @@ -41,6 +47,9 @@ func New(cfg Config) *Sender { if strings.TrimSpace(cfg.From) == "" { cfg.From = cfg.Username } + if strings.TrimSpace(cfg.AppName) == "" { + cfg.AppName = "telesrv" + } return &Sender{cfg: cfg} } @@ -52,8 +61,7 @@ func (s *Sender) Deliver(ctx context.Context, req otpdelivery.Request) (otpdeliv return otpdelivery.Result{}, fmt.Errorf("smtp cannot deliver channel %q", req.Channel) } ttl := time.Until(req.ExpiresAt) - subject := "Your telesrv login code" - body := fmt.Sprintf("Your telesrv login code is %s.\n\nThis code expires in %s. If you did not request it, ignore this email.\n", req.Code, humanTTL(ttl)) + subject, body := emailContent(s.cfg.AppName, req.Code, ttl) if err := s.send(ctx, req.Recipient, subject, body); err != nil { return otpdelivery.Result{}, err } @@ -147,6 +155,15 @@ func buildMessage(from, to, subject, body string) []byte { return b.Bytes() } +// emailContent builds the login-code email subject/body, branded with the +// operator's configured product name (Config.AppName) instead of the +// package's internal "telesrv" fallback. +func emailContent(appName, code string, ttl time.Duration) (subject, body string) { + subject = fmt.Sprintf("Your %s login code", appName) + body = fmt.Sprintf("Your %s login code is %s.\n\nThis code expires in %s. If you did not request it, ignore this email.\n", appName, code, humanTTL(ttl)) + return subject, body +} + func humanTTL(ttl time.Duration) string { if ttl <= 0 { return "a short time" diff --git a/internal/otpdelivery/smtp/sender_test.go b/internal/otpdelivery/smtp/sender_test.go index b515dd34..3858caf2 100644 --- a/internal/otpdelivery/smtp/sender_test.go +++ b/internal/otpdelivery/smtp/sender_test.go @@ -29,3 +29,30 @@ func TestHumanTTLRoundsNetworkSkew(t *testing.T) { t.Fatalf("humanTTL = %q", got) } } + +// TestEmailContentUsesConfiguredAppName asserts the login-code email is +// branded with the operator's configured product name, not the package's +// internal "telesrv" fallback — the subject/body previously always said +// "Your telesrv login code" regardless of Config.AppName. +func TestEmailContentUsesConfiguredAppName(t *testing.T) { + subject, body := emailContent("OwpenGram", "12345", 5*time.Minute) + if subject != "Your OwpenGram login code" { + t.Fatalf("subject = %q, want %q", subject, "Your OwpenGram login code") + } + if !strings.Contains(body, "Your OwpenGram login code is 12345.") { + t.Fatalf("body missing branded code line: %q", body) + } + if strings.Contains(subject, "telesrv") || strings.Contains(body, "telesrv") { + t.Fatalf("email still mentions telesrv instead of the configured app name:\nsubject=%q\nbody=%q", subject, body) + } +} + +// TestNewDefaultsAppNameToTelesrvWhenUnset asserts the package's own +// self-contained fallback (used only if a caller forgets to pass AppName) +// still works, mirroring FromName/TLSMode/Timeout's existing defaulting. +func TestNewDefaultsAppNameToTelesrvWhenUnset(t *testing.T) { + sender := New(Config{Host: "smtp.example.test", Port: 25, From: "noreply@example.test"}) + if sender.cfg.AppName != "telesrv" { + t.Fatalf("cfg.AppName = %q, want default %q", sender.cfg.AppName, "telesrv") + } +}