package usecase import ( "bytes" "context" "fmt" "html/template" "strings" "apps/backend/internal/module/notification/domain" "github.com/zeromicro/go-zero/core/logx" ) // Config for notification service. type Config struct { Sender string DevExposeCode bool Brand domain.Brand Providers []domain.EmailDelivery } // Service implements domain.UseCase. type Service struct { cfg Config } func NewService(cfg Config) *Service { if cfg.Sender == "" { cfg.Sender = "Harbor Desk " } if cfg.Brand.Name == "" { cfg.Brand = domain.DefaultBrand(cfg.Brand.Link) } if len(cfg.Providers) == 0 { cfg.Providers = []domain.EmailDelivery{NewLogDelivery()} cfg.DevExposeCode = true } return &Service{cfg: cfg} } func (s *Service) SenderFrom() string { return s.cfg.Sender } func (s *Service) ExposeCodeInAPI() bool { return s.cfg.DevExposeCode } func (s *Service) Brand() domain.Brand { return s.cfg.Brand } func (s *Service) Enabled() bool { for _, p := range s.cfg.Providers { if p.Name() != "log" { return true } } return false } func (s *Service) GetEmailTemplate(kind domain.TemplateKind, lang domain.Language) (*domain.EmailTemplate, error) { lang = domain.NormalizeLanguage(string(lang)) return domain.GetTemplate(kind, lang, s.cfg.Brand) } func (s *Service) RenderVerify(kind domain.TemplateKind, lang domain.Language, vars domain.VerifyTemplateVars) (string, string, error) { lang = domain.NormalizeLanguage(string(lang)) tpl, err := s.GetEmailTemplate(kind, lang) if err != nil { return "", "", err } if vars.Username == "" { vars.Username = "there" } // hermes may leave empty ResetURL button — still OK if placeholder replaced t, err := template.New(string(kind)).Parse(tpl.Body) if err != nil { return "", "", err } var buf bytes.Buffer if err := t.Execute(&buf, vars); err != nil { return "", "", err } return tpl.Subject, buf.String(), nil } func (s *Service) SendEmail(ctx context.Context, req domain.EmailRequest) error { if req.SenderEmail == "" { req.SenderEmail = s.cfg.Sender } var last error for _, p := range s.cfg.Providers { if err := p.SendEmail(ctx, req); err != nil { logx.WithContext(ctx).Errorf("notification send via %s failed: %v", p.Name(), err) last = err continue } logx.WithContext(ctx).Infof("notification sent via %s to %s subject=%q", p.Name(), maskEmail(req.RecipientEmail), req.Subject) return nil } if last != nil { return last } return fmt.Errorf("no email providers configured") } func maskEmail(e string) string { e = strings.TrimSpace(e) at := strings.IndexByte(e, '@') if at <= 1 { return "***" } return e[:1] + "***" + e[at:] }