Browse Source

System-wide webhooks (#10546)

* Create system webhook column (and migration)

* Create system webhook DB methods

Based on the default webhook ones

* Modify router to handle system webhooks and default ones

* Remove old unused admin nav template

* Adjust orgRepoCtx to differentiate system and default webhook URLs

* Assign IsSystemWebhook when creating webhooks

* Correctly use booleans for IsSystemWebhook

* Use system webhooks when preparing webhooks for payload

* Add UI and locale changes

* Use router params to differentiate admin hook pages

* Fix deleting admin webhooks and rename method

* Add clarity to webhook docs

* Revert "Remove old unused admin nav template"

This reverts commit 191a20a738.

* Rename WebHooksNewPost to GiteaHooksNewPost for clarity

* Reintroduce blank line lost during merge conflict

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
tags/v1.12.0-dev
James Lakin 4 years ago
parent
commit
a9f4489bbc
No account linked to committer's email address

+ 5
- 5
docs/content/doc/features/webhooks.en-us.md View File



# Webhooks # 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 - Gogs
- Slack - Slack
- Discord - Discord
- Dingtalk - Dingtalk
- Telegram - Telegram
- Microsoft Teams - Microsoft Teams
- Feishu


### Event information ### Event information


The following is an example of event information that will be sent by Gitea to The following is an example of event information that will be sent by Gitea to
a Payload URL: a Payload URL:



``` ```
X-GitHub-Delivery: f6266f16-1bf3-46a5-9ea4-602e06ead473 X-GitHub-Delivery: f6266f16-1bf3-46a5-9ea4-602e06ead473
X-GitHub-Event: push X-GitHub-Event: push

+ 2
- 0
models/migrations/migrations.go View File

NewMigration("remove dependencies from deleted repositories", purgeUnusedDependencies), NewMigration("remove dependencies from deleted repositories", purgeUnusedDependencies),
// v130 -> v131 // v130 -> v131
NewMigration("Expand webhooks for more granularity", expandWebhooks), NewMigration("Expand webhooks for more granularity", expandWebhooks),
// v131 -> v132
NewMigration("Add IsSystemWebhook column to webhooks table", addSystemWebhookColumn),
} }


// Migrate database to current version // Migrate database to current version

+ 22
- 0
models/migrations/v131.go View File

// 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
}

+ 46
- 19
models/webhook.go View File



// Webhook represents a web hook object. // Webhook represents a web hook object.
type Webhook struct { 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"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
func GetDefaultWebhook(id int64) (*Webhook, error) { func GetDefaultWebhook(id int64) (*Webhook, error) {
webhook := &Webhook{ID: id} webhook := &Webhook{ID: id}
has, err := x. 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) Get(webhook)
if err != nil { if err != nil {
return nil, err return nil, err
func getDefaultWebhooks(e Engine) ([]*Webhook, error) { func getDefaultWebhooks(e Engine) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5) webhooks := make([]*Webhook, 0, 5)
return webhooks, e. 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) Find(&webhooks)
} }


}) })
} }


// 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() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if err := sess.Begin(); err != nil { if err := sess.Begin(); err != nil {

+ 7
- 0
modules/webhook/webhook.go View File

ws = append(ws, orgHooks...) 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 { if len(ws) == 0 {
return nil return nil
} }

+ 5
- 0
options/locale/locale_en-US.ini View File

organizations = Organizations organizations = Organizations
repositories = Repositories repositories = Repositories
hooks = Default Webhooks hooks = Default Webhooks
systemhooks = System Webhooks
authentication = Authentication Sources authentication = Authentication Sources
emails = User Emails emails = User Emails
config = Configuration config = Configuration
hooks.add_webhook = Add Default Webhook hooks.add_webhook = Add Default Webhook
hooks.update_webhook = Update 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.auth_manage_panel = Authentication Source Management
auths.new = Add Authentication Source auths.new = Add Authentication Source
auths.name = Name auths.name = Name

+ 34
- 15
routers/admin/hooks.go View File

) )


const ( const (
// tplAdminHooks template path for render hook settings
// tplAdminHooks template path to render hook settings
tplAdminHooks base.TplName = "admin/hooks" 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 { if err != nil {
ctx.ServerError("GetWebhooksDefaults", err)
ctx.ServerError("GetWebhooksAdmin", err)
return return
} }


ctx.HTML(200, tplAdminHooks) 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()) ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error())
} else { } else {
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) 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",
})
}
} }

+ 102
- 77
routers/repo/webhook.go View File

} }


type orgRepoCtx struct { 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) { func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
if len(ctx.Repo.RepoLink) > 0 { if len(ctx.Repo.RepoLink) > 0 {
return &orgRepoCtx{ return &orgRepoCtx{
} }


if ctx.User.IsAdmin { 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{ 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 }, nil
} }


return 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["PageIsAdminHooks"] = true
ctx.Data["PageIsAdminHooksNew"] = true ctx.Data["PageIsAdminHooksNew"] = true
} else { } else {
} }
} }


// 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["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true ctx.Data["PageIsSettingsHooksNew"] = true
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
} }


w := &models.Webhook{ 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 { if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err) ctx.ServerError("UpdateEvent", err)
w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
} else if orCtx.OrgID > 0 { } else if orCtx.OrgID > 0 {
w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id")) w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
} else if orCtx.IsSystemWebhook {
w, err = models.GetSystemWebhook(ctx.ParamsInt64(":id"))
} else { } else {
w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id")) w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id"))
} }

+ 6
- 6
routers/routes/routes.go View File

m.Post("/delete", admin.DeleteRepo) 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.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("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Get("", org.Webhooks) m.Get("", org.Webhooks)
m.Post("/delete", org.DeleteWebhook) m.Post("/delete", org.DeleteWebhook)
m.Get("/:type/new", repo.WebhooksNew) 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("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Get("", repo.Webhooks) m.Get("", repo.Webhooks)
m.Post("/delete", repo.DeleteWebhook) m.Post("/delete", repo.DeleteWebhook)
m.Get("/:type/new", repo.WebhooksNew) 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("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)

+ 3
- 0
templates/admin/navbar.tmpl View File

<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> <a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}} {{.i18n.Tr "admin.hooks"}}
</a> </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"> <a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths">
{{.i18n.Tr "admin.authentication"}} {{.i18n.Tr "admin.authentication"}}
</a> </a>

Loading…
Cancel
Save