diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 34c3ee9db5466..003f7c47d5b4a 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -508,6 +508,8 @@ AUTO_WATCH_NEW_REPOS = true AUTO_WATCH_ON_CHANGES = false [webhook] +; Supported webhook types +SUPPORTED_TYPES = gitea, gogs, slack, discord, dingtalk, telegram, msteams ; Hook task queue length, increase if webhook shooting starts hanging QUEUE_LENGTH = 1000 ; Deliver timeout in seconds diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index ab353f9d5aa6f..a77d666e8a879 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -311,6 +311,7 @@ relation to port exhaustion. ## Webhook (`webhook`) +- `SUPPORTED_TYPES`: **gitea, gogs, slack, discord, dingtalk, telegram, msteams** Supported webhook types. - `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value. - `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks. - `SKIP_TLS_VERIFY`: **false**: Allow insecure certification. diff --git a/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md b/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md index b545d9a99d1f3..26ee94d4892dd 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md +++ b/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md @@ -125,6 +125,7 @@ menu: ## Webhook (`webhook`) +- `SUPPORTED_TYPES`: **gitea, gogs, slack, discord, dingtalk, telegram, msteams** 支持的 webhook 类型。 - `QUEUE_LENGTH`: 说明: Hook 任务队列长度。 - `DELIVER_TIMEOUT`: 请求webhooks的超时时间,单位秒。 - `SKIP_TLS_VERIFY`: 是否允许不安全的证书。 diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go index 4a953616f1524..aa323721a9691 100644 --- a/modules/setting/webhook.go +++ b/modules/setting/webhook.go @@ -25,6 +25,7 @@ var ( QueueLength: 1000, DeliverTimeout: 5, SkipTLSVerify: false, + Types: []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"}, PagingNum: 10, ProxyURL: "", ProxyHosts: []string{}, @@ -33,10 +34,15 @@ var ( func newWebhookService() { sec := Cfg.Section("webhook") + Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5) Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool() - Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"} + defaults := Webhook.Types + Webhook.Types = sec.Key("SUPPORTED_TYPES").Strings(",") + if len(Webhook.Types) == 0 { + Webhook.Types = defaults + } Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("") if Webhook.ProxyURL != "" { diff --git a/modules/webhook/deliver.go b/modules/webhook/deliver.go index b262505cead15..3f017c2f190cc 100644 --- a/modules/webhook/deliver.go +++ b/modules/webhook/deliver.go @@ -215,8 +215,8 @@ func webhookProxy() func(req *http.Request) (*url.URL, error) { } } -// InitDeliverHooks starts the hooks delivery thread -func InitDeliverHooks() { +// initDeliverHooks starts the hooks delivery thread +func initDeliverHooks() { timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second webhookHTTPClient = &http.Client{ diff --git a/modules/webhook/dingtalk.go b/modules/webhook/dingtalk.go index b6d58f55cfea4..0c4ab1a84bfb8 100644 --- a/modules/webhook/dingtalk.go +++ b/modules/webhook/dingtalk.go @@ -392,8 +392,17 @@ func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error) return nil, nil } -// GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload -func GetDingtalkPayload(p api.Payloader, event models.HookEventType, meta string) (*DingtalkPayload, error) { +type DingtalkWebhookType struct { +} + +var _ WebhookType = &DingtalkWebhookType{} + +func (dingtalkType *DingtalkWebhookType) Name() string { + return "dingtalk" +} + +// GetPayload converts a ding talk webhook into a DingtalkPayload +func (dingtalkType *DingtalkWebhookType) GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { s := new(DingtalkPayload) switch event { diff --git a/modules/webhook/discord.go b/modules/webhook/discord.go index f92157a1ab4f8..b0314225c76cf 100644 --- a/modules/webhook/discord.go +++ b/modules/webhook/discord.go @@ -544,8 +544,17 @@ func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*Discor }, nil } -// GetDiscordPayload converts a discord webhook into a DiscordPayload -func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) (*DiscordPayload, error) { +type DiscordWebhookType struct { +} + +var _ WebhookType = &DiscordWebhookType{} + +func (discordType *DiscordWebhookType) Name() string { + return "discord" +} + +// GetPayload converts a discord webhook into a DiscordPayload +func (discordType *DiscordWebhookType) GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { s := new(DiscordPayload) discord := &DiscordMeta{} diff --git a/modules/webhook/msteams.go b/modules/webhook/msteams.go index 2636e299830cf..5473528c490ee 100644 --- a/modules/webhook/msteams.go +++ b/modules/webhook/msteams.go @@ -699,8 +699,17 @@ func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) { }, nil } -// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload -func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (*MSTeamsPayload, error) { +type MSTeamsWebhookType struct { +} + +var _ WebhookType = &MSTeamsWebhookType{} + +func (msteamsType *MSTeamsWebhookType) Name() string { + return "msteams" +} + +// GetPayload converts a MSTeams webhook into a MSTeamsPayload +func (msteamsType *MSTeamsWebhookType) GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { s := new(MSTeamsPayload) switch event { diff --git a/modules/webhook/slack.go b/modules/webhook/slack.go index 7d844bfa50644..142586eb1fd27 100644 --- a/modules/webhook/slack.go +++ b/modules/webhook/slack.go @@ -398,8 +398,17 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla }, nil } -// GetSlackPayload converts a slack webhook into a SlackPayload -func GetSlackPayload(p api.Payloader, event models.HookEventType, meta string) (*SlackPayload, error) { +type SlackWebhookType struct { +} + +var _ WebhookType = &SlackWebhookType{} + +func (slackType *SlackWebhookType) Name() string { + return "slack" +} + +// GetPayload converts a slack webhook into a SlackPayload +func (slackType *SlackWebhookType) GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { s := new(SlackPayload) slack := &SlackMeta{} diff --git a/modules/webhook/telegram.go b/modules/webhook/telegram.go index 4e67b22954a3e..11663e5a28f05 100644 --- a/modules/webhook/telegram.go +++ b/modules/webhook/telegram.go @@ -307,8 +307,17 @@ func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error) return nil, nil } -// GetTelegramPayload converts a telegram webhook into a TelegramPayload -func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string) (*TelegramPayload, error) { +type TelegramWebhookType struct { +} + +var _ WebhookType = &TelegramWebhookType{} + +func (telegramType *TelegramWebhookType) Name() string { + return "telegram" +} + +// GetPayload converts a telegram webhook into a TelegramPayload +func (telegramType *TelegramWebhookType) GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { s := new(TelegramPayload) switch event { diff --git a/modules/webhook/webhook.go b/modules/webhook/webhook.go index 410e47461fe68..d52dff55c62f5 100644 --- a/modules/webhook/webhook.go +++ b/modules/webhook/webhook.go @@ -88,33 +88,13 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo var payloader api.Payloader var err error // Use separate objects so modifications won't be made on payload on non-Gogs/Gitea type hooks. - switch w.HookTaskType { - case models.SLACK: - payloader, err = GetSlackPayload(p, event, w.Meta) + webhookType, ok := webhookTypes[w.HookTaskType.Name()] + if ok { + payloader, err = webhookType.GetPayload(p, event, w.Meta) if err != nil { - return fmt.Errorf("GetSlackPayload: %v", err) + return fmt.Errorf("GetPayload: %v", err) } - case models.DISCORD: - payloader, err = GetDiscordPayload(p, event, w.Meta) - if err != nil { - return fmt.Errorf("GetDiscordPayload: %v", err) - } - case models.DINGTALK: - payloader, err = GetDingtalkPayload(p, event, w.Meta) - if err != nil { - return fmt.Errorf("GetDingtalkPayload: %v", err) - } - case models.TELEGRAM: - payloader, err = GetTelegramPayload(p, event, w.Meta) - if err != nil { - return fmt.Errorf("GetTelegramPayload: %v", err) - } - case models.MSTEAMS: - payloader, err = GetMSTeamsPayload(p, event, w.Meta) - if err != nil { - return fmt.Errorf("GetMSTeamsPayload: %v", err) - } - default: + } else { p.SetSecret(w.Secret) payloader = p } @@ -187,3 +167,18 @@ func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api. } return nil } + +// Init initlize +func Init() error { + for _, tp := range setting.Webhook.Types { + for _, wt := range defaultWebhookTypes { + if strings.EqualFold(wt.Name(), tp) { + RegisterWebhookType(wt) + break + } + } + } + + initDeliverHooks() + return nil +} diff --git a/modules/webhook/webhook_type.go b/modules/webhook/webhook_type.go new file mode 100644 index 0000000000000..fd772e7c18f05 --- /dev/null +++ b/modules/webhook/webhook_type.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package webhook + +import ( + "code.gitea.io/gitea/models" + api "code.gitea.io/gitea/modules/structs" +) + +// WebhookType represents a webhook type, all webhook types should implement this interface +type WebhookType interface { + Name() string + GetPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) +} + +var ( + webhookTypes map[string]WebhookType + defaultWebhookTypes = []WebhookType{ + &SlackWebhookType{}, + &TelegramWebhookType{}, + &DingtalkWebhookType{}, + &DiscordWebhookType{}, + &MSTeamsWebhookType{}, + } +) + +func init() { + webhookTypes = make(map[string]WebhookType) +} + +// RegisterWebhookType register a webhook type +func RegisterWebhookType(t WebhookType) { + webhookTypes[t.Name()] = t +} diff --git a/routers/init.go b/routers/init.go index 4623f02e66193..4b69e8d78b5be 100644 --- a/routers/init.go +++ b/routers/init.go @@ -102,7 +102,9 @@ func GlobalInit() { issue_indexer.InitIssueIndexer(false) models.InitRepoIndexer() mirror_service.InitSyncMirrors() - webhook.InitDeliverHooks() + if err := webhook.Init(); err != nil { + log.Fatal("Failed to initialize webhook: %v", err) + } models.InitTestPullRequests() if err := task.Init(); err != nil { log.Fatal("Failed to initialize task scheduler: %v", err)