package usecase import ( "fmt" "time" "gateway/internal/library/mongo" redislib "gateway/internal/library/redis" notifconfig "gateway/internal/model/notification/config" domtpl "gateway/internal/model/notification/domain/template" domusecase "gateway/internal/model/notification/domain/usecase" "gateway/internal/model/notification/provider/email" "gateway/internal/model/notification/provider/sms" ) // FactoryParam wires notification module dependencies for production or tests. type FactoryParam struct { MongoConf *mongo.Conf Redis *redislib.Client // process-wide client from svc; nil → memory idempotency/quota, no async worker Config notifconfig.Config } // NewNotifierUseCaseFromParam builds NotifierUseCase when Mongo is configured. func NewNotifierUseCaseFromParam(param FactoryParam) (domusecase.NotifierUseCase, error) { mod, err := NewModuleFromParam(param) if err != nil { return nil, err } return mod.Notifier, nil } func buildEmailChain(cfg notifconfig.Config, rc *redislib.Client) (*email.Chain, error) { senders, err := collectEmailSenders(cfg) if err != nil { return nil, err } if len(senders) == 0 { switch cfg.Email.Provider { case "", notifconfig.ProviderMock: opts := []email.MockSenderOption{email.WithMockName(notifconfig.ProviderMock)} if r := rc.Zero(); r != nil { opts = append(opts, email.WithMockRedis(r, 0)) } return email.NewChain(email.NewMockSender(opts...)), nil default: return nil, fmt.Errorf("notification: no email senders enabled and provider %q is not mock", cfg.Email.Provider) } } opts := []email.ChainOption{} if cfg.Email.SES.Enable { opts = append(opts, email.WithTimeout(90*time.Second)) } if len(opts) == 0 { return email.NewChain(senders...), nil } return email.NewChainWithOptions(senders, opts...), nil } func collectEmailSenders(cfg notifconfig.Config) ([]email.Sender, error) { var senders []email.Sender if cfg.Email.SMTP.Enable { s, err := email.NewSMTPSender(email.SMTPSettings{ Sort: cfg.Email.SMTP.Sort, Host: cfg.Email.SMTP.Host, Port: cfg.Email.SMTP.Port, Username: cfg.Email.SMTP.Username, Password: cfg.Email.SMTP.Password, }) if err != nil { return nil, fmt.Errorf("notification: smtp: %w", err) } senders = append(senders, s) } if cfg.Email.SES.Enable { s, err := email.NewSESSender(email.SESSettings{ Sort: cfg.Email.SES.Sort, Region: cfg.Email.SES.Region, AccessKey: cfg.Email.SES.AccessKey, SecretKey: cfg.Email.SES.SecretKey, SessionToken: cfg.Email.SES.SessionToken, }) if err != nil { return nil, fmt.Errorf("notification: ses: %w", err) } senders = append(senders, s) } return senders, nil } func buildSMSChain(cfg notifconfig.Config, rc *redislib.Client) (*sms.Chain, error) { senders, err := collectSMSSenders(cfg) if err != nil { return nil, err } if len(senders) == 0 { switch cfg.SMS.Provider { case "", notifconfig.ProviderMock: opts := []sms.MockSenderOption{sms.WithMockName(notifconfig.ProviderMock)} if r := rc.Zero(); r != nil { opts = append(opts, sms.WithMockRedis(r, 0)) } return sms.NewChain(sms.NewMockSender(opts...)), nil default: return nil, fmt.Errorf("notification: no sms senders enabled and provider %q is not mock", cfg.SMS.Provider) } } return sms.NewChain(senders...), nil } func collectSMSSenders(cfg notifconfig.Config) ([]sms.Sender, error) { var senders []sms.Sender if cfg.SMS.Mitake.Enable { s, err := sms.NewMitakeSender(sms.MitakeSettings{ Sort: cfg.SMS.Mitake.Sort, User: cfg.SMS.Mitake.User, Password: cfg.SMS.Mitake.Password, }, nil) if err != nil { return nil, fmt.Errorf("notification: mitake: %w", err) } senders = append(senders, s) } return senders, nil } func fallbackLocales(defaultLocale string) []string { if defaultLocale == "" { return []string{domtpl.LocaleZhTW, domtpl.LocaleEnUS} } if defaultLocale == domtpl.LocaleZhTW { return []string{domtpl.LocaleZhTW, domtpl.LocaleEnUS} } return []string{defaultLocale, domtpl.LocaleZhTW, domtpl.LocaleEnUS} }