Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System-wide webhooks #10546

Merged
merged 19 commits into from
Mar 8, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/content/doc/features/webhooks.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ var migrations = []Migration{
NewMigration("Fix topic repository count", fixTopicRepositoryCount),
// v127 -> v128
NewMigration("add repository code language statistics", addLanguageStats),
// v128 -> v129
NewMigration("Add IsSystemWebhook column to webhooks table", addSystemWebhookColumn),
}

// Migrate database to current version
Expand Down
22 changes: 22 additions & 0 deletions models/migrations/v128.go
Original file line number Diff line number Diff line change
@@ -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
}
65 changes: 46 additions & 19 deletions models/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,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"`
Expand Down Expand Up @@ -315,7 +316,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
Expand All @@ -333,7 +334,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)
}

Expand Down Expand Up @@ -385,8 +412,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 {
Expand Down
7 changes: 7 additions & 0 deletions modules/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
5 changes: 5 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,7 @@ users = User Accounts
organizations = Organizations
repositories = Repositories
hooks = Default Webhooks
systemhooks = System Webhooks
authentication = Authentication Sources
config = Configuration
notices = System Notices
Expand Down Expand Up @@ -1844,6 +1845,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
Expand Down
49 changes: 34 additions & 15 deletions routers/admin/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,55 @@ 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
}

ctx.Data["Webhooks"] = ws
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",
})
}
}
Loading