mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09: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