mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	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:
		@@ -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 New Issue
	
	Block a user