From 35de8619f2789d4bb428f715e9097b96824f63ee Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Fri, 29 Aug 2025 07:47:38 +0800 Subject: [PATCH] refactor: notification providers factory --- internal/notify/providers.go | 173 +------------------- internal/notify/providers/registry.go | 47 ++++++ internal/notify/providers/sp_dingtalkbot.go | 27 +++ internal/notify/providers/sp_discordbot.go | 27 +++ internal/notify/providers/sp_email.go | 33 ++++ internal/notify/providers/sp_larkbot.go | 26 +++ internal/notify/providers/sp_mattermost.go | 29 ++++ internal/notify/providers/sp_slackbot.go | 27 +++ internal/notify/providers/sp_telegrambot.go | 27 +++ internal/notify/providers/sp_webhook.go | 52 ++++++ internal/notify/providers/sp_wecombot.go | 26 +++ 11 files changed, 329 insertions(+), 165 deletions(-) create mode 100644 internal/notify/providers/registry.go create mode 100644 internal/notify/providers/sp_dingtalkbot.go create mode 100644 internal/notify/providers/sp_discordbot.go create mode 100644 internal/notify/providers/sp_email.go create mode 100644 internal/notify/providers/sp_larkbot.go create mode 100644 internal/notify/providers/sp_mattermost.go create mode 100644 internal/notify/providers/sp_slackbot.go create mode 100644 internal/notify/providers/sp_telegrambot.go create mode 100644 internal/notify/providers/sp_webhook.go create mode 100644 internal/notify/providers/sp_wecombot.go diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 58ef7a91f..306c49090 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -1,22 +1,9 @@ package notify import ( - "fmt" - "net/http" - "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/internal/notify/providers" "github.com/certimate-go/certimate/pkg/core" - pDingTalkBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/dingtalkbot" - pDiscordBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/discordbot" - pEmail "github.com/certimate-go/certimate/pkg/core/notifier/providers/email" - pLarkBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/larkbot" - pMattermost "github.com/certimate-go/certimate/pkg/core/notifier/providers/mattermost" - pSlackBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/slackbot" - pTelegramBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/telegrambot" - pWebhook "github.com/certimate-go/certimate/pkg/core/notifier/providers/webhook" - pWeComBot "github.com/certimate-go/certimate/pkg/core/notifier/providers/wecombot" - xhttp "github.com/certimate-go/certimate/pkg/utils/http" - xmaps "github.com/certimate-go/certimate/pkg/utils/maps" ) type notifierProviderOptions struct { @@ -26,157 +13,13 @@ type notifierProviderOptions struct { } func createNotifierProvider(options *notifierProviderOptions) (core.Notifier, error) { - /* - 注意:如果追加新的常量值,请保持以 ASCII 排序。 - NOTICE: If you add new constant, please keep ASCII order. - */ - switch options.Provider { - case domain.NotificationProviderTypeDingTalkBot: - { - access := domain.AccessConfigForDingTalkBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pDingTalkBot.NewNotifierProvider(&pDingTalkBot.NotifierProviderConfig{ - WebhookUrl: access.WebhookUrl, - Secret: access.Secret, - }) - } - - case domain.NotificationProviderTypeDiscordBot: - { - access := domain.AccessConfigForDiscordBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pDiscordBot.NewNotifierProvider(&pDiscordBot.NotifierProviderConfig{ - BotToken: access.BotToken, - ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId), - }) - } - - case domain.NotificationProviderTypeEmail: - { - access := domain.AccessConfigForEmail{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pEmail.NewNotifierProvider(&pEmail.NotifierProviderConfig{ - SmtpHost: access.SmtpHost, - SmtpPort: access.SmtpPort, - SmtpTls: access.SmtpTls, - Username: access.Username, - Password: access.Password, - SenderAddress: access.SenderAddress, - SenderName: access.SenderName, - ReceiverAddress: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.ReceiverAddress), - }) - } - - case domain.NotificationProviderTypeLarkBot: - { - access := domain.AccessConfigForLarkBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pLarkBot.NewNotifierProvider(&pLarkBot.NotifierProviderConfig{ - WebhookUrl: access.WebhookUrl, - }) - } - - case domain.NotificationProviderTypeMattermost: - { - access := domain.AccessConfigForMattermost{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pMattermost.NewNotifierProvider(&pMattermost.NotifierProviderConfig{ - ServerUrl: access.ServerUrl, - Username: access.Username, - Password: access.Password, - ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId), - }) - } - - case domain.NotificationProviderTypeSlackBot: - { - access := domain.AccessConfigForSlackBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pSlackBot.NewNotifierProvider(&pSlackBot.NotifierProviderConfig{ - BotToken: access.BotToken, - ChannelId: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.ChannelId), - }) - } - - case domain.NotificationProviderTypeTelegramBot: - { - access := domain.AccessConfigForTelegramBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pTelegramBot.NewNotifierProvider(&pTelegramBot.NotifierProviderConfig{ - BotToken: access.BotToken, - ChatId: xmaps.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.ChatId), - }) - } - - case domain.NotificationProviderTypeWebhook: - { - access := domain.AccessConfigForWebhook{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - mergedHeaders := make(map[string]string) - if defaultHeadersString := access.HeadersString; defaultHeadersString != "" { - h, err := xhttp.ParseHeaders(defaultHeadersString) - if err != nil { - return nil, fmt.Errorf("failed to parse webhook headers: %w", err) - } - for key := range h { - mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) - } - } - if extendedHeadersString := xmaps.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" { - h, err := xhttp.ParseHeaders(extendedHeadersString) - if err != nil { - return nil, fmt.Errorf("failed to parse webhook headers: %w", err) - } - for key := range h { - mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) - } - } - - return pWebhook.NewNotifierProvider(&pWebhook.NotifierProviderConfig{ - WebhookUrl: access.Url, - WebhookData: xmaps.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DataString), - Method: access.Method, - Headers: mergedHeaders, - AllowInsecureConnections: access.AllowInsecureConnections, - }) - } - - case domain.NotificationProviderTypeWeComBot: - { - access := domain.AccessConfigForWeComBot{} - if err := xmaps.Populate(options.ProviderAccessConfig, &access); err != nil { - return nil, fmt.Errorf("failed to populate provider access config: %w", err) - } - - return pWeComBot.NewNotifierProvider(&pWeComBot.NotifierProviderConfig{ - WebhookUrl: access.WebhookUrl, - }) - } + provider, err := providers.Registries.Get(options.Provider) + if err != nil { + return nil, err } - return nil, fmt.Errorf("unsupported notifier provider '%s'", options.Provider) + return provider(&providers.ProviderFactoryOptions{ + AccessConfig: options.ProviderAccessConfig, + ProviderConfig: options.ProviderServiceConfig, + }) } diff --git a/internal/notify/providers/registry.go b/internal/notify/providers/registry.go new file mode 100644 index 000000000..7c8ac70df --- /dev/null +++ b/internal/notify/providers/registry.go @@ -0,0 +1,47 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" +) + +type ProviderFactoryFunc func(options *ProviderFactoryOptions) (core.Notifier, error) + +type ProviderFactoryOptions struct { + AccessConfig map[string]any + ProviderConfig map[string]any +} + +type Registry[T comparable] interface { + Register(T, ProviderFactoryFunc) error + Get(T) (ProviderFactoryFunc, error) +} + +type registry[T comparable] struct { + factories map[T]ProviderFactoryFunc +} + +func (r *registry[T]) Register(name T, factory ProviderFactoryFunc) error { + if _, exists := r.factories[name]; exists { + return fmt.Errorf("provider '%v' already registered", name) + } + + r.factories[name] = factory + return nil +} + +func (r *registry[T]) Get(name T) (ProviderFactoryFunc, error) { + if factory, exists := r.factories[name]; exists { + return factory, nil + } + + return nil, fmt.Errorf("provider '%v' not registered", name) +} + +func newRegistry[T comparable]() Registry[T] { + return ®istry[T]{factories: make(map[T]ProviderFactoryFunc)} +} + +var Registries = newRegistry[domain.NotificationProviderType]() diff --git a/internal/notify/providers/sp_dingtalkbot.go b/internal/notify/providers/sp_dingtalkbot.go new file mode 100644 index 000000000..d524632a5 --- /dev/null +++ b/internal/notify/providers/sp_dingtalkbot.go @@ -0,0 +1,27 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/dingtalkbot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeDingTalkBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForDingTalkBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := dingtalkbot.NewNotifierProvider(&dingtalkbot.NotifierProviderConfig{ + WebhookUrl: access.WebhookUrl, + Secret: access.Secret, + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_discordbot.go b/internal/notify/providers/sp_discordbot.go new file mode 100644 index 000000000..d098f46c1 --- /dev/null +++ b/internal/notify/providers/sp_discordbot.go @@ -0,0 +1,27 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/discordbot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeDiscordBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForDiscordBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := discordbot.NewNotifierProvider(&discordbot.NotifierProviderConfig{ + BotToken: access.BotToken, + ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId), + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_email.go b/internal/notify/providers/sp_email.go new file mode 100644 index 000000000..92203dc10 --- /dev/null +++ b/internal/notify/providers/sp_email.go @@ -0,0 +1,33 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/email" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeEmail, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForEmail{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := email.NewNotifierProvider(&email.NotifierProviderConfig{ + SmtpHost: access.SmtpHost, + SmtpPort: access.SmtpPort, + SmtpTls: access.SmtpTls, + Username: access.Username, + Password: access.Password, + SenderAddress: access.SenderAddress, + SenderName: access.SenderName, + ReceiverAddress: xmaps.GetOrDefaultString(options.ProviderConfig, "receiverAddress", access.ReceiverAddress), + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_larkbot.go b/internal/notify/providers/sp_larkbot.go new file mode 100644 index 000000000..82f761b22 --- /dev/null +++ b/internal/notify/providers/sp_larkbot.go @@ -0,0 +1,26 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/larkbot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeLarkBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForLarkBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := larkbot.NewNotifierProvider(&larkbot.NotifierProviderConfig{ + WebhookUrl: access.WebhookUrl, + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_mattermost.go b/internal/notify/providers/sp_mattermost.go new file mode 100644 index 000000000..a04a77396 --- /dev/null +++ b/internal/notify/providers/sp_mattermost.go @@ -0,0 +1,29 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/mattermost" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeMattermost, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForMattermost{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := mattermost.NewNotifierProvider(&mattermost.NotifierProviderConfig{ + ServerUrl: access.ServerUrl, + Username: access.Username, + Password: access.Password, + ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId), + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_slackbot.go b/internal/notify/providers/sp_slackbot.go new file mode 100644 index 000000000..5cde5aee0 --- /dev/null +++ b/internal/notify/providers/sp_slackbot.go @@ -0,0 +1,27 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + slackbot "github.com/certimate-go/certimate/pkg/core/notifier/providers/slackbot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeSlackBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForSlackBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := slackbot.NewNotifierProvider(&slackbot.NotifierProviderConfig{ + BotToken: access.BotToken, + ChannelId: xmaps.GetOrDefaultString(options.ProviderConfig, "channelId", access.ChannelId), + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_telegrambot.go b/internal/notify/providers/sp_telegrambot.go new file mode 100644 index 000000000..ddd8f739b --- /dev/null +++ b/internal/notify/providers/sp_telegrambot.go @@ -0,0 +1,27 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/telegrambot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeTelegramBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForTelegramBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := telegrambot.NewNotifierProvider(&telegrambot.NotifierProviderConfig{ + BotToken: access.BotToken, + ChatId: xmaps.GetOrDefaultInt64(options.ProviderConfig, "chatId", access.ChatId), + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_webhook.go b/internal/notify/providers/sp_webhook.go new file mode 100644 index 000000000..0f289bb70 --- /dev/null +++ b/internal/notify/providers/sp_webhook.go @@ -0,0 +1,52 @@ +package providers + +import ( + "fmt" + "net/http" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/webhook" + xhttp "github.com/certimate-go/certimate/pkg/utils/http" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeWebhook, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForWebhook{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + mergedHeaders := make(map[string]string) + if defaultHeadersString := access.HeadersString; defaultHeadersString != "" { + h, err := xhttp.ParseHeaders(defaultHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + if extendedHeadersString := xmaps.GetString(options.ProviderConfig, "headers"); extendedHeadersString != "" { + h, err := xhttp.ParseHeaders(extendedHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + + provider, err := webhook.NewNotifierProvider(&webhook.NotifierProviderConfig{ + WebhookUrl: access.Url, + WebhookData: xmaps.GetOrDefaultString(options.ProviderConfig, "webhookData", access.DataString), + Method: access.Method, + Headers: mergedHeaders, + AllowInsecureConnections: access.AllowInsecureConnections, + }) + return provider, err + }); err != nil { + panic(err) + } +} diff --git a/internal/notify/providers/sp_wecombot.go b/internal/notify/providers/sp_wecombot.go new file mode 100644 index 000000000..a19456f13 --- /dev/null +++ b/internal/notify/providers/sp_wecombot.go @@ -0,0 +1,26 @@ +package providers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + "github.com/certimate-go/certimate/pkg/core/notifier/providers/wecombot" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + if err := Registries.Register(domain.NotificationProviderTypeWeComBot, func(options *ProviderFactoryOptions) (core.Notifier, error) { + access := domain.AccessConfigForWeComBot{} + if err := xmaps.Populate(options.AccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := wecombot.NewNotifierProvider(&wecombot.NotifierProviderConfig{ + WebhookUrl: access.WebhookUrl, + }) + return provider, err + }); err != nil { + panic(err) + } +}