aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKN4CK3R <admin@oldschoolhack.me>2022-01-05 22:00:20 +0100
committerGitHub <noreply@github.com>2022-01-05 15:00:20 -0600
commitbf7b083cfe47cc922090ce7922b89f7a5030a44d (patch)
tree62291bc31a1dd28252a8802555d09085d30416ee
parenta38ba634a4da15fbb2d1b6ac6742cf01c1503ea4 (diff)
downloadgitea-bf7b083cfe47cc922090ce7922b89f7a5030a44d.tar.gz
gitea-bf7b083cfe47cc922090ce7922b89f7a5030a44d.zip
Add replay of webhooks. (#18191)
-rw-r--r--models/webhook/hooktask.go39
-rw-r--r--models/webhook/webhook.go16
-rw-r--r--options/locale/locale_en-US.ini3
-rw-r--r--routers/web/repo/webhook.go24
-rw-r--r--routers/web/web.go17
-rw-r--r--services/webhook/webhook.go12
-rw-r--r--templates/admin/hook_new.tmpl2
-rw-r--r--templates/repo/settings/webhook/history.tmpl8
8 files changed, 108 insertions, 13 deletions
diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index 1967ded298..1d19ebd24e 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -175,18 +175,13 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error {
- return createHookTask(db.GetEngine(db.DefaultContext), t)
-}
-
-func createHookTask(e db.Engine, t *HookTask) error {
data, err := t.Payloader.JSONPayload()
if err != nil {
return err
}
t.UUID = gouuid.New().String()
t.PayloadContent = string(data)
- _, err = e.Insert(t)
- return err
+ return db.Insert(db.DefaultContext, t)
}
// UpdateHookTask updates information of hook task.
@@ -195,6 +190,38 @@ func UpdateHookTask(t *HookTask) error {
return err
}
+// ReplayHookTask copies a hook task to get re-delivered
+func ReplayHookTask(hookID int64, uuid string) (*HookTask, error) {
+ var newTask *HookTask
+
+ err := db.WithTx(func(ctx context.Context) error {
+ task := &HookTask{
+ HookID: hookID,
+ UUID: uuid,
+ }
+ has, err := db.GetByBean(ctx, task)
+ if err != nil {
+ return err
+ } else if !has {
+ return ErrHookTaskNotExist{
+ HookID: hookID,
+ UUID: uuid,
+ }
+ }
+
+ newTask = &HookTask{
+ UUID: gouuid.New().String(),
+ RepoID: task.RepoID,
+ HookID: task.HookID,
+ PayloadContent: task.PayloadContent,
+ EventType: task.EventType,
+ }
+ return db.Insert(ctx, newTask)
+ })
+
+ return newTask, err
+}
+
// FindUndeliveredHookTasks represents find the undelivered hook tasks
func FindUndeliveredHookTasks() ([]*HookTask, error) {
tasks := make([]*HookTask, 0, 10)
diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go
index d01f548eed..21c01d9289 100644
--- a/models/webhook/webhook.go
+++ b/models/webhook/webhook.go
@@ -41,6 +41,22 @@ func (err ErrWebhookNotExist) Error() string {
return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
}
+// ErrHookTaskNotExist represents a "HookTaskNotExist" kind of error.
+type ErrHookTaskNotExist struct {
+ HookID int64
+ UUID string
+}
+
+// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
+func IsErrHookTaskNotExist(err error) bool {
+ _, ok := err.(ErrHookTaskNotExist)
+ return ok
+}
+
+func (err ErrHookTaskNotExist) Error() string {
+ return fmt.Sprintf("hook task does not exist [hook: %d, uuid: %s]", err.HookID, err.UUID)
+}
+
// HookContentType is the content type of a web hook
type HookContentType int
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 7a3dbd50a8..3d60df5d68 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1831,12 +1831,13 @@ settings.webhook_deletion_desc = Removing a webhook deletes its settings and del
settings.webhook_deletion_success = The webhook has been removed.
settings.webhook.test_delivery = Test Delivery
settings.webhook.test_delivery_desc = Test this webhook with a fake event.
-settings.webhook.test_delivery_success = A fake event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history.
settings.webhook.request = Request
settings.webhook.response = Response
settings.webhook.headers = Headers
settings.webhook.payload = Content
settings.webhook.body = Body
+settings.webhook.replay.description = Replay this webhook.
+settings.webhook.delivery.success = An event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history.
settings.githooks_desc = "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations."
settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.
settings.githook_name = Hook Name
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 47d8413671..2ec2e8911f 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -1189,11 +1189,33 @@ func TestWebhook(ctx *context.Context) {
ctx.Flash.Error("PrepareWebhook: " + err.Error())
ctx.Status(500)
} else {
- ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
+ ctx.Flash.Info(ctx.Tr("repo.settings.webhook.delivery.success"))
ctx.Status(200)
}
}
+// ReplayWebhook replays a webhook
+func ReplayWebhook(ctx *context.Context) {
+ hookTaskUUID := ctx.Params(":uuid")
+
+ orCtx, w := checkWebhook(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ if err := webhook_service.ReplayHookTask(w, hookTaskUUID); err != nil {
+ if webhook.IsErrHookTaskNotExist(err) {
+ ctx.NotFound("ReplayHookTask", nil)
+ } else {
+ ctx.ServerError("ReplayHookTask", err)
+ }
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.webhook.delivery.success"))
+ ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
+}
+
// DeleteWebhook delete a webhook
func DeleteWebhook(ctx *context.Context) {
if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
diff --git a/routers/web/web.go b/routers/web/web.go
index e35ccb5aab..8abdf7c61f 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -435,7 +435,10 @@ func RegisterRoutes(m *web.Route) {
m.Group("/hooks", func() {
m.Get("", admin.DefaultOrSystemWebhooks)
m.Post("/delete", admin.DeleteDefaultOrSystemWebhook)
- m.Get("/{id}", repo.WebHooksEdit)
+ m.Group("/{id}", func() {
+ m.Get("", repo.WebHooksEdit)
+ m.Post("/replay/{uuid}", repo.ReplayWebhook)
+ })
m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
@@ -559,7 +562,10 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
- m.Get("/{id}", repo.WebHooksEdit)
+ m.Group("/{id}", func() {
+ m.Get("", repo.WebHooksEdit)
+ m.Post("/replay/{uuid}", repo.ReplayWebhook)
+ })
m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
@@ -653,8 +659,11 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
- m.Get("/{id}", repo.WebHooksEdit)
- m.Post("/{id}/test", repo.TestWebhook)
+ m.Group("/{id}", func() {
+ m.Get("", repo.WebHooksEdit)
+ m.Post("/test", repo.TestWebhook)
+ m.Post("/replay/{uuid}", repo.ReplayWebhook)
+ })
m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go
index 9356f4ee11..f284a20c30 100644
--- a/services/webhook/webhook.go
+++ b/services/webhook/webhook.go
@@ -229,3 +229,15 @@ func prepareWebhooks(repo *repo_model.Repository, event webhook_model.HookEventT
}
return nil
}
+
+// ReplayHookTask replays a webhook task
+func ReplayHookTask(w *webhook_model.Webhook, uuid string) error {
+ t, err := webhook_model.ReplayHookTask(w.ID, uuid)
+ if err != nil {
+ return err
+ }
+
+ go hookQueue.Add(t.RepoID)
+
+ return nil
+}
diff --git a/templates/admin/hook_new.tmpl b/templates/admin/hook_new.tmpl
index 4710498b20..2cd3fc826c 100644
--- a/templates/admin/hook_new.tmpl
+++ b/templates/admin/hook_new.tmpl
@@ -1,5 +1,5 @@
{{template "base/head" .}}
-<div class="page-content admin new webhook">
+<div class="page-content admin settings new webhook">
{{template "admin/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index 8823d6c1dc..a719e4453a 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -40,6 +40,14 @@
<span class="ui label">N/A</span>
{{end}}
</a>
+ {{if or $.Permission.IsAdmin $.IsOrganizationOwner $.PageIsAdmin}}
+ <div class="right menu">
+ <form class="item" action="{{$.Link}}/replay/{{.UUID}}" method="post">
+ {{$.CsrfTokenHtml}}
+ <button class="ui tiny button tooltip" data-content="{{$.i18n.Tr "repo.settings.webhook.replay.description"}}" data-variation="inverted tiny">{{svg "octicon-sync"}}</button>
+ </form>
+ </div>
+ {{end}}
</div>
<div class="ui bottom attached tab segment active" data-tab="request-{{.ID}}">
{{if .RequestInfo}}