diff options
-rw-r--r-- | docs/content/doc/features/webhooks.en-us.md | 10 | ||||
-rw-r--r-- | models/migrations/migrations.go | 2 | ||||
-rw-r--r-- | models/migrations/v131.go | 22 | ||||
-rw-r--r-- | models/webhook.go | 65 | ||||
-rw-r--r-- | modules/webhook/webhook.go | 7 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 5 | ||||
-rw-r--r-- | routers/admin/hooks.go | 49 | ||||
-rw-r--r-- | routers/repo/webhook.go | 179 | ||||
-rw-r--r-- | routers/routes/routes.go | 12 | ||||
-rw-r--r-- | templates/admin/navbar.tmpl | 3 |
10 files changed, 232 insertions, 122 deletions
diff --git a/docs/content/doc/features/webhooks.en-us.md b/docs/content/doc/features/webhooks.en-us.md index 1a0a180e7a..ec50c013f5 100644 --- a/docs/content/doc/features/webhooks.en-us.md +++ b/docs/content/doc/features/webhooks.en-us.md @@ -15,24 +15,24 @@ menu: # Webhooks -Gitea supports web hooks for repository events. This can be found in the settings -page `/:username/:reponame/settings/hooks`. All event pushes are POST requests. -The methods currently supported are: +Gitea supports web hooks for repository events. This can be configured in the settings +page `/:username/:reponame/settings/hooks` by a repository admin. Webhooks can also be configured on a per-organization and whole system basis. +All event pushes are POST requests. The methods currently supported are: -- Gitea +- Gitea (can also be a GET request) - Gogs - Slack - Discord - Dingtalk - Telegram - Microsoft Teams +- Feishu ### Event information The following is an example of event information that will be sent by Gitea to a Payload URL: - ``` X-GitHub-Delivery: f6266f16-1bf3-46a5-9ea4-602e06ead473 X-GitHub-Event: push diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ba411dd8c2..2badb72788 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -194,6 +194,8 @@ var migrations = []Migration{ NewMigration("remove dependencies from deleted repositories", purgeUnusedDependencies), // v130 -> v131 NewMigration("Expand webhooks for more granularity", expandWebhooks), + // v131 -> v132 + NewMigration("Add IsSystemWebhook column to webhooks table", addSystemWebhookColumn), } // Migrate database to current version diff --git a/models/migrations/v131.go b/models/migrations/v131.go new file mode 100644 index 0000000000..a38c7be634 --- /dev/null +++ b/models/migrations/v131.go @@ -0,0 +1,22 @@ +// Copyright 2020 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 migrations + +import ( + "fmt" + + "xorm.io/xorm" +) + +func addSystemWebhookColumn(x *xorm.Engine) error { + type Webhook struct { + IsSystemWebhook bool `xorm:"NOT NULL DEFAULT false"` + } + + if err := x.Sync2(new(Webhook)); err != nil { + return fmt.Errorf("Sync2: %v", err) + } + return nil +} diff --git a/models/webhook.go b/models/webhook.go index 82aedf7e81..d161ca1ae9 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -99,21 +99,22 @@ const ( // Webhook represents a web hook object. type Webhook struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX"` - OrgID int64 `xorm:"INDEX"` - URL string `xorm:"url TEXT"` - Signature string `xorm:"TEXT"` - HTTPMethod string `xorm:"http_method"` - ContentType HookContentType - Secret string `xorm:"TEXT"` - Events string `xorm:"TEXT"` - *HookEvent `xorm:"-"` - IsSSL bool `xorm:"is_ssl"` - IsActive bool `xorm:"INDEX"` - HookTaskType HookTaskType - Meta string `xorm:"TEXT"` // store hook-specific attributes - LastStatus HookStatus // Last delivery status + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook + OrgID int64 `xorm:"INDEX"` + IsSystemWebhook bool + URL string `xorm:"url TEXT"` + Signature string `xorm:"TEXT"` + HTTPMethod string `xorm:"http_method"` + ContentType HookContentType + Secret string `xorm:"TEXT"` + Events string `xorm:"TEXT"` + *HookEvent `xorm:"-"` + IsSSL bool `xorm:"is_ssl"` + IsActive bool `xorm:"INDEX"` + HookTaskType HookTaskType + Meta string `xorm:"TEXT"` // store hook-specific attributes + LastStatus HookStatus // Last delivery status CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` @@ -401,7 +402,7 @@ func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error func GetDefaultWebhook(id int64) (*Webhook, error) { webhook := &Webhook{ID: id} has, err := x. - Where("repo_id=? AND org_id=?", 0, 0). + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false). Get(webhook) if err != nil { return nil, err @@ -419,7 +420,33 @@ func GetDefaultWebhooks() ([]*Webhook, error) { func getDefaultWebhooks(e Engine) ([]*Webhook, error) { webhooks := make([]*Webhook, 0, 5) return webhooks, e. - Where("repo_id=? AND org_id=?", 0, 0). + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false). + Find(&webhooks) +} + +// GetSystemWebhook returns admin system webhook by given ID. +func GetSystemWebhook(id int64) (*Webhook, error) { + webhook := &Webhook{ID: id} + has, err := x. + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). + Get(webhook) + if err != nil { + return nil, err + } else if !has { + return nil, ErrWebhookNotExist{id} + } + return webhook, nil +} + +// GetSystemWebhooks returns all admin system webhooks. +func GetSystemWebhooks() ([]*Webhook, error) { + return getSystemWebhooks(x) +} + +func getSystemWebhooks(e Engine) ([]*Webhook, error) { + webhooks := make([]*Webhook, 0, 5) + return webhooks, e. + Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). Find(&webhooks) } @@ -471,8 +498,8 @@ func DeleteWebhookByOrgID(orgID, id int64) error { }) } -// DeleteDefaultWebhook deletes an admin-default webhook by given ID. -func DeleteDefaultWebhook(id int64) error { +// DeleteDefaultSystemWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0) +func DeleteDefaultSystemWebhook(id int64) error { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { diff --git a/modules/webhook/webhook.go b/modules/webhook/webhook.go index 2fab0803bc..75a81d2aff 100644 --- a/modules/webhook/webhook.go +++ b/modules/webhook/webhook.go @@ -181,6 +181,13 @@ func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api. ws = append(ws, orgHooks...) } + // Add any admin-defined system webhooks + systemHooks, err := models.GetSystemWebhooks() + if err != nil { + return fmt.Errorf("GetSystemWebhooks: %v", err) + } + ws = append(ws, systemHooks...) + if len(ws) == 0 { return nil } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bf5b03d47d..483970e032 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1753,6 +1753,7 @@ users = User Accounts organizations = Organizations repositories = Repositories hooks = Default Webhooks +systemhooks = System Webhooks authentication = Authentication Sources emails = User Emails config = Configuration @@ -1889,6 +1890,10 @@ hooks.desc = Webhooks automatically make HTTP POST requests to a server when cer hooks.add_webhook = Add Default Webhook hooks.update_webhook = Update Default Webhook +systemhooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined will act on all repositories on the system, so please consider any performance implications this may have. Read more in the <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">webhooks guide</a>. +systemhooks.add_webhook = Add System Webhook +systemhooks.update_webhook = Update System Webhook + auths.auth_manage_panel = Authentication Source Management auths.new = Add Authentication Source auths.name = Name diff --git a/routers/admin/hooks.go b/routers/admin/hooks.go index b80ed3cc3c..4697c4d933 100644 --- a/routers/admin/hooks.go +++ b/routers/admin/hooks.go @@ -12,20 +12,32 @@ import ( ) const ( - // tplAdminHooks template path for render hook settings + // tplAdminHooks template path to render hook settings tplAdminHooks base.TplName = "admin/hooks" ) -// DefaultWebhooks render admin-default webhook list page -func DefaultWebhooks(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("admin.hooks") - ctx.Data["PageIsAdminHooks"] = true - ctx.Data["BaseLink"] = setting.AppSubURL + "/admin/hooks" - ctx.Data["Description"] = ctx.Tr("admin.hooks.desc") +// DefaultOrSystemWebhooks renders both admin default and system webhook list pages +func DefaultOrSystemWebhooks(ctx *context.Context) { + var ws []*models.Webhook + var err error + + // Are we looking at default webhooks? + if ctx.Params(":configType") == "hooks" { + ctx.Data["Title"] = ctx.Tr("admin.hooks") + ctx.Data["Description"] = ctx.Tr("admin.hooks.desc") + ctx.Data["PageIsAdminHooks"] = true + ctx.Data["BaseLink"] = setting.AppSubURL + "/admin/hooks" + ws, err = models.GetDefaultWebhooks() + } else { + ctx.Data["Title"] = ctx.Tr("admin.systemhooks") + ctx.Data["Description"] = ctx.Tr("admin.systemhooks.desc") + ctx.Data["PageIsAdminSystemHooks"] = true + ctx.Data["BaseLink"] = setting.AppSubURL + "/admin/system-hooks" + ws, err = models.GetSystemWebhooks() + } - ws, err := models.GetDefaultWebhooks() if err != nil { - ctx.ServerError("GetWebhooksDefaults", err) + ctx.ServerError("GetWebhooksAdmin", err) return } @@ -33,15 +45,22 @@ func DefaultWebhooks(ctx *context.Context) { ctx.HTML(200, tplAdminHooks) } -// DeleteDefaultWebhook response for delete admin-default webhook -func DeleteDefaultWebhook(ctx *context.Context) { - if err := models.DeleteDefaultWebhook(ctx.QueryInt64("id")); err != nil { +// DeleteDefaultOrSystemWebhook handler to delete an admin-defined system or default webhook +func DeleteDefaultOrSystemWebhook(ctx *context.Context) { + if err := models.DeleteDefaultSystemWebhook(ctx.QueryInt64("id")); err != nil { ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) } - ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/admin/hooks", - }) + // Are we looking at default webhooks? + if ctx.Params(":configType") == "hooks" { + ctx.JSON(200, map[string]interface{}{ + "redirect": setting.AppSubURL + "/admin/hooks", + }) + } else { + ctx.JSON(200, map[string]interface{}{ + "redirect": setting.AppSubURL + "/admin/system-hooks", + }) + } } diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index cf6ff27542..94c610fe54 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -49,14 +49,15 @@ func Webhooks(ctx *context.Context) { } type orgRepoCtx struct { - OrgID int64 - RepoID int64 - IsAdmin bool - Link string - NewTemplate base.TplName + OrgID int64 + RepoID int64 + IsAdmin bool + IsSystemWebhook bool + Link string + NewTemplate base.TplName } -// getOrgRepoCtx determines whether this is a repo, organization, or admin context. +// getOrgRepoCtx determines whether this is a repo, organization, or admin (both default and system) context. func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) { if len(ctx.Repo.RepoLink) > 0 { return &orgRepoCtx{ @@ -75,10 +76,21 @@ func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) { } if ctx.User.IsAdmin { + // Are we looking at default webhooks? + if ctx.Params(":configType") == "hooks" { + return &orgRepoCtx{ + IsAdmin: true, + Link: path.Join(setting.AppSubURL, "/admin/hooks"), + NewTemplate: tplAdminHookNew, + }, nil + } + + // Must be system webhooks instead return &orgRepoCtx{ - IsAdmin: true, - Link: path.Join(setting.AppSubURL, "/admin/hooks"), - NewTemplate: tplAdminHookNew, + IsAdmin: true, + IsSystemWebhook: true, + Link: path.Join(setting.AppSubURL, "/admin/system-hooks"), + NewTemplate: tplAdminHookNew, }, nil } @@ -105,7 +117,10 @@ func WebhooksNew(ctx *context.Context) { return } - if orCtx.IsAdmin { + if orCtx.IsAdmin && orCtx.IsSystemWebhook { + ctx.Data["PageIsAdminSystemHooks"] = true + ctx.Data["PageIsAdminSystemHooksNew"] = true + } else if orCtx.IsAdmin { ctx.Data["PageIsAdminHooks"] = true ctx.Data["PageIsAdminHooksNew"] = true } else { @@ -159,8 +174,8 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { } } -// WebHooksNewPost response for creating webhook -func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { +// GiteaHooksNewPost response for creating Gitea webhook +func GiteaHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -185,15 +200,16 @@ func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - HTTPMethod: form.HTTPMethod, - ContentType: contentType, - Secret: form.Secret, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.GITEA, - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + HTTPMethod: form.HTTPMethod, + ContentType: contentType, + Secret: form.Secret, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.GITEA, + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -238,14 +254,15 @@ func newGogsWebhookPost(ctx *context.Context, form auth.NewGogshookForm, kind mo } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: contentType, - Secret: form.Secret, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: kind, - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: contentType, + Secret: form.Secret, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: kind, + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -287,14 +304,15 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.DISCORD, - Meta: string(meta), - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.DISCORD, + Meta: string(meta), + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -327,14 +345,15 @@ func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.DINGTALK, - Meta: "", - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.DINGTALK, + Meta: "", + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -376,14 +395,15 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID), - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.TELEGRAM, - Meta: string(meta), - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID), + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.TELEGRAM, + Meta: string(meta), + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -416,14 +436,15 @@ func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.MSTEAMS, - Meta: "", - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.MSTEAMS, + Meta: "", + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -473,14 +494,15 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.SLACK, - Meta: string(meta), - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.SLACK, + Meta: string(meta), + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -513,14 +535,15 @@ func FeishuHooksNewPost(ctx *context.Context, form auth.NewFeishuHookForm) { } w := &models.Webhook{ - RepoID: orCtx.RepoID, - URL: form.PayloadURL, - ContentType: models.ContentTypeJSON, - HookEvent: ParseHookEvent(form.WebhookForm), - IsActive: form.Active, - HookTaskType: models.FEISHU, - Meta: "", - OrgID: orCtx.OrgID, + RepoID: orCtx.RepoID, + URL: form.PayloadURL, + ContentType: models.ContentTypeJSON, + HookEvent: ParseHookEvent(form.WebhookForm), + IsActive: form.Active, + HookTaskType: models.FEISHU, + Meta: "", + OrgID: orCtx.OrgID, + IsSystemWebhook: orCtx.IsSystemWebhook, } if err := w.UpdateEvent(); err != nil { ctx.ServerError("UpdateEvent", err) @@ -549,6 +572,8 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) { w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) } else if orCtx.OrgID > 0 { w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id")) + } else if orCtx.IsSystemWebhook { + w, err = models.GetSystemWebhook(ctx.ParamsInt64(":id")) } else { w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id")) } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 0b0b4e05a3..093edcd920 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -458,11 +458,11 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/delete", admin.DeleteRepo) }) - m.Group("/hooks", func() { - m.Get("", admin.DefaultWebhooks) - m.Post("/delete", admin.DeleteDefaultWebhook) + m.Group("/^:configType(hooks|system-hooks)$", func() { + m.Get("", admin.DefaultOrSystemWebhooks) + m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) @@ -569,7 +569,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", org.Webhooks) m.Post("/delete", org.DeleteWebhook) m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) @@ -635,7 +635,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", repo.Webhooks) m.Post("/delete", repo.DeleteWebhook) m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index 546df22e12..6d81d7557f 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -14,6 +14,9 @@ <a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> {{.i18n.Tr "admin.hooks"}} </a> + <a class="{{if .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/system-hooks"> + {{.i18n.Tr "admin.systemhooks"}} + </a> <a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths"> {{.i18n.Tr "admin.authentication"}} </a> |