Add GET requests to webhook (#6771)
* Add GET requests to webhook * make fmt * Handle invalid http method on webhook * Uppercase http method in webhook * Rename v85.go to v86.go * make fmt
This commit is contained in:
parent
55a8e12d85
commit
a2a006a5d5
8 changed files with 71 additions and 15 deletions
|
@ -225,6 +225,8 @@ var migrations = []Migration{
|
||||||
NewMigration("add table to store original imported gpg keys", addGPGKeyImport),
|
NewMigration("add table to store original imported gpg keys", addGPGKeyImport),
|
||||||
// v85 -> v86
|
// v85 -> v86
|
||||||
NewMigration("hash application token", hashAppToken),
|
NewMigration("hash application token", hashAppToken),
|
||||||
|
// v86 -> v87
|
||||||
|
NewMigration("add http method to webhook", addHTTPMethodToWebhook),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate database to current version
|
// Migrate database to current version
|
||||||
|
|
17
models/migrations/v86.go
Normal file
17
models/migrations/v86.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// 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 migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addHTTPMethodToWebhook(x *xorm.Engine) error {
|
||||||
|
type Webhook struct {
|
||||||
|
HTTPMethod string `xorm:"http_method DEFAULT 'POST'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync2(new(Webhook))
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ type Webhook struct {
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `xorm:"INDEX"`
|
||||||
URL string `xorm:"url TEXT"`
|
URL string `xorm:"url TEXT"`
|
||||||
Signature string `xorm:"TEXT"`
|
Signature string `xorm:"TEXT"`
|
||||||
|
HTTPMethod string `xorm:"http_method"`
|
||||||
ContentType HookContentType
|
ContentType HookContentType
|
||||||
Secret string `xorm:"TEXT"`
|
Secret string `xorm:"TEXT"`
|
||||||
Events string `xorm:"TEXT"`
|
Events string `xorm:"TEXT"`
|
||||||
|
@ -553,6 +555,7 @@ type HookTask struct {
|
||||||
Signature string `xorm:"TEXT"`
|
Signature string `xorm:"TEXT"`
|
||||||
api.Payloader `xorm:"-"`
|
api.Payloader `xorm:"-"`
|
||||||
PayloadContent string `xorm:"TEXT"`
|
PayloadContent string `xorm:"TEXT"`
|
||||||
|
HTTPMethod string `xorm:"http_method"`
|
||||||
ContentType HookContentType
|
ContentType HookContentType
|
||||||
EventType HookEventType
|
EventType HookEventType
|
||||||
IsSSL bool
|
IsSSL bool
|
||||||
|
@ -707,6 +710,7 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
|
||||||
URL: w.URL,
|
URL: w.URL,
|
||||||
Signature: signature,
|
Signature: signature,
|
||||||
Payloader: payloader,
|
Payloader: payloader,
|
||||||
|
HTTPMethod: w.HTTPMethod,
|
||||||
ContentType: w.ContentType,
|
ContentType: w.ContentType,
|
||||||
EventType: event,
|
EventType: event,
|
||||||
IsSSL: w.IsSSL,
|
IsSSL: w.IsSSL,
|
||||||
|
@ -751,9 +755,32 @@ func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payl
|
||||||
|
|
||||||
func (t *HookTask) deliver() {
|
func (t *HookTask) deliver() {
|
||||||
t.IsDelivered = true
|
t.IsDelivered = true
|
||||||
|
t.RequestInfo = &HookRequest{
|
||||||
|
Headers: map[string]string{},
|
||||||
|
}
|
||||||
|
t.ResponseInfo = &HookResponse{
|
||||||
|
Headers: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
|
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
|
||||||
req := httplib.Post(t.URL).SetTimeout(timeout, timeout).
|
|
||||||
|
var req *httplib.Request
|
||||||
|
if t.HTTPMethod == http.MethodPost {
|
||||||
|
req = httplib.Post(t.URL)
|
||||||
|
switch t.ContentType {
|
||||||
|
case ContentTypeJSON:
|
||||||
|
req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
|
||||||
|
case ContentTypeForm:
|
||||||
|
req.Param("payload", t.PayloadContent)
|
||||||
|
}
|
||||||
|
} else if t.HTTPMethod == http.MethodGet {
|
||||||
|
req = httplib.Get(t.URL).Param("payload", t.PayloadContent)
|
||||||
|
} else {
|
||||||
|
t.ResponseInfo.Body = fmt.Sprintf("Invalid http method: %v", t.HTTPMethod)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.SetTimeout(timeout, timeout).
|
||||||
Header("X-Gitea-Delivery", t.UUID).
|
Header("X-Gitea-Delivery", t.UUID).
|
||||||
Header("X-Gitea-Event", string(t.EventType)).
|
Header("X-Gitea-Event", string(t.EventType)).
|
||||||
Header("X-Gitea-Signature", t.Signature).
|
Header("X-Gitea-Signature", t.Signature).
|
||||||
|
@ -764,25 +791,11 @@ func (t *HookTask) deliver() {
|
||||||
HeaderWithSensitiveCase("X-GitHub-Event", string(t.EventType)).
|
HeaderWithSensitiveCase("X-GitHub-Event", string(t.EventType)).
|
||||||
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify})
|
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify})
|
||||||
|
|
||||||
switch t.ContentType {
|
|
||||||
case ContentTypeJSON:
|
|
||||||
req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
|
|
||||||
case ContentTypeForm:
|
|
||||||
req.Param("payload", t.PayloadContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record delivery information.
|
// Record delivery information.
|
||||||
t.RequestInfo = &HookRequest{
|
|
||||||
Headers: map[string]string{},
|
|
||||||
}
|
|
||||||
for k, vals := range req.Headers() {
|
for k, vals := range req.Headers() {
|
||||||
t.RequestInfo.Headers[k] = strings.Join(vals, ",")
|
t.RequestInfo.Headers[k] = strings.Join(vals, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.ResponseInfo = &HookResponse{
|
|
||||||
Headers: map[string]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
t.Delivered = time.Now().UnixNano()
|
t.Delivered = time.Now().UnixNano()
|
||||||
if t.IsSucceed {
|
if t.IsSucceed {
|
||||||
|
|
|
@ -196,6 +196,7 @@ func (f WebhookForm) ChooseEvents() bool {
|
||||||
// NewWebhookForm form for creating web hook
|
// NewWebhookForm form for creating web hook
|
||||||
type NewWebhookForm struct {
|
type NewWebhookForm struct {
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
HTTPMethod string `binding:"Required;In(POST,GET)"`
|
||||||
ContentType int `binding:"Required"`
|
ContentType int `binding:"Required"`
|
||||||
Secret string
|
Secret string
|
||||||
WebhookForm
|
WebhookForm
|
||||||
|
|
|
@ -1192,6 +1192,7 @@ settings.githook_content = Hook Content
|
||||||
settings.update_githook = Update Hook
|
settings.update_githook = Update Hook
|
||||||
settings.add_webhook_desc = Gitea will send <code>POST</code> requests with a specified content type to the target URL. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
|
settings.add_webhook_desc = Gitea will send <code>POST</code> requests with a specified content type to the target URL. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
|
||||||
settings.payload_url = Target URL
|
settings.payload_url = Target URL
|
||||||
|
settings.http_method = HTTP Method
|
||||||
settings.content_type = POST Content Type
|
settings.content_type = POST Content Type
|
||||||
settings.secret = Secret
|
settings.secret = Secret
|
||||||
settings.slack_username = Username
|
settings.slack_username = Username
|
||||||
|
|
|
@ -1430,6 +1430,15 @@ function initWebhook() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var updateContentType = function () {
|
||||||
|
var visible = $('#http_method').val() === 'POST';
|
||||||
|
$('#content_type').parent().parent()[visible ? 'show' : 'hide']();
|
||||||
|
};
|
||||||
|
updateContentType();
|
||||||
|
$('#http_method').change(function () {
|
||||||
|
updateContentType();
|
||||||
|
});
|
||||||
|
|
||||||
// Test delivery
|
// Test delivery
|
||||||
$('#test-delivery').click(function () {
|
$('#test-delivery').click(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
|
|
|
@ -176,6 +176,7 @@ func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
|
||||||
w := &models.Webhook{
|
w := &models.Webhook{
|
||||||
RepoID: orCtx.RepoID,
|
RepoID: orCtx.RepoID,
|
||||||
URL: form.PayloadURL,
|
URL: form.PayloadURL,
|
||||||
|
HTTPMethod: form.HTTPMethod,
|
||||||
ContentType: contentType,
|
ContentType: contentType,
|
||||||
Secret: form.Secret,
|
Secret: form.Secret,
|
||||||
HookEvent: ParseHookEvent(form.WebhookForm),
|
HookEvent: ParseHookEvent(form.WebhookForm),
|
||||||
|
|
|
@ -6,6 +6,18 @@
|
||||||
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
|
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
|
||||||
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{.i18n.Tr "repo.settings.http_method"}}</label>
|
||||||
|
<div class="ui selection dropdown">
|
||||||
|
<input type="hidden" id="http_method" name="http_method" value="{{if .Webhook.HTTPMethod}}{{.Webhook.HTTPMethod}}{{else}}POST{{end}}">
|
||||||
|
<div class="default text"></div>
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="POST">POST</div>
|
||||||
|
<div class="item" data-value="GET">GET</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{.i18n.Tr "repo.settings.content_type"}}</label>
|
<label>{{.i18n.Tr "repo.settings.content_type"}}</label>
|
||||||
<div class="ui selection dropdown">
|
<div class="ui selection dropdown">
|
||||||
|
|
Reference in a new issue