From 8d6a9a0263bb089fe62277629adf30263a34b28f Mon Sep 17 00:00:00 2001 From: Nick Guenther Date: Sat, 25 Feb 2023 03:23:58 -0500 Subject: [PATCH] Let API create and edit system webhooks "System" webhooks -- webhooks run on all repos on a Gitea instance -- were added in https://github.com/go-gitea/gitea/pull/14537 (I believe?) but managing them by the API is buggy. - In routers/api/v1/utils/hook.go, correctly handle the distinction between system and default webhooks. This enables actually creating, editing and deleting both kinds. - In routers/api/, move `/api/v1/admin/hooks` to `/api/v1/admin/hooks/{system,default}`. This allows users to access the code in the previous point. - In routers/web/, move `/admin/{system,default}-hooks` and most of `/admin/hooks/` into `/admin/hooks/{system,default}` to match API. - In model/, normalize vocabulary. Since the new sub-type, the terminology has been a confusing mix of "SystemWebhook", "DefaultSystemWebhook", "SystemOrDefaultWebhook" and "DefaultWebhook". Standardize on "AdminWebhook" everywhere with `isSystemWebhook bool` to separate the two sub-types. - Using a bool made it easier to handle both cases without duplicating the router endpoints - Make PATCH /admin/hooks/{system,default}/:id appropriately return 404. Fixes https://github.com/go-gitea/gitea/issues/23139. Supersedes https://github.com/go-gitea/gitea/pull/23142. --- models/webhook/webhook_system.go | 39 ++++---- routers/api/v1/admin/hooks.go | 90 +++++++++++++------ routers/api/v1/api.go | 2 +- routers/api/v1/utils/hook.go | 59 +++++++----- routers/web/admin/hooks.go | 23 ++--- routers/web/repo/setting/webhook.go | 20 +++-- routers/web/web.go | 19 ++-- .../repo/settings/webhook/base_list.tmpl | 2 +- templates/swagger/v1_json.tmpl | 67 +++++++++++--- 9 files changed, 212 insertions(+), 109 deletions(-) diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index 2e89f9547bba2..e645b6d86bc52 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -11,19 +11,11 @@ import ( "code.gitea.io/gitea/modules/util" ) -// GetDefaultWebhooks returns all admin-default webhooks. -func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, false). - Find(&webhooks) -} - -// GetSystemOrDefaultWebhook returns admin system or default webhook by given ID. -func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) { +// GetSystemWebhook returns admin default webhook by given ID. +func GetAdminWebhook(ctx context.Context, id int64, isSystemWebhook bool) (*Webhook, error) { webhook := &Webhook{ID: id} has, err := db.GetEngine(ctx). - Where("repo_id=? AND owner_id=?", 0, 0). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook). Get(webhook) if err != nil { return nil, err @@ -35,22 +27,37 @@ func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) // GetSystemWebhooks returns all admin system webhooks. func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webhook, error) { + return GetAdminWebhooks(ctx, true, isActive) +} + +// GetDefaultWebhooks returns all webhooks that are copied to new repos. +func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { + return GetAdminWebhooks(ctx, false, util.OptionalBoolNone) +} + +// returns all admin system or default webhooks. +// isSystemWebhook == true gives system webhooks, otherwise gives default webhooks. +// isActive filters system webhooks to those currently enabled or disabled; pass util.OptionalBoolNone to get both. +func GetAdminWebhooks(ctx context.Context, isSystemWebhook bool, isActive util.OptionalBool) ([]*Webhook, error) { + if !isSystemWebhook && isActive.IsTrue() { + return nil, fmt.Errorf("GetAdminWebhooks: active (isActive) default (!isSystemWebhook) hooks are impossible") + } webhooks := make([]*Webhook, 0, 5) if isActive.IsNone() { return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook). Find(&webhooks) } return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). + Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, isSystemWebhook, isActive.IsTrue()). Find(&webhooks) } -// DeleteDefaultSystemWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0) -func DeleteDefaultSystemWebhook(ctx context.Context, id int64) error { +// DeleteWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0) +func DeleteAdminWebhook(ctx context.Context, id int64, isSystemWebhook bool) error { return db.WithTx(ctx, func(ctx context.Context) error { count, err := db.GetEngine(ctx). - Where("repo_id=? AND owner_id=?", 0, 0). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook). Delete(&Webhook{ID: id}) if err != nil { return err diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index 8a095a7defa2a..42e02806c7a0e 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -17,33 +17,34 @@ import ( webhook_service "code.gitea.io/gitea/services/webhook" ) -// ListHooks list system's webhooks +// list system or default webhooks func ListHooks(ctx *context.APIContext) { - // swagger:operation GET /admin/hooks admin adminListHooks + // swagger:operation GET /admin/hooks/{configType} admin adminListHooks // --- // summary: List system's webhooks // produces: // - application/json // parameters: - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer + // - name: configType + // in: path + // description: whether the hook is system-wide or copied-to-each-new-repo + // type: string + // enum: [system, default] + // required: true // responses: // "200": // "$ref": "#/responses/HookList" - sysHooks, err := webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone) + isSystemWebhook := ctx.Params(":configType") == "system" + + adminHooks, err := webhook.GetAdminWebhooks(ctx, isSystemWebhook, util.OptionalBoolNone) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) + ctx.Error(http.StatusInternalServerError, "GetAdminWebhooks", err) return } - hooks := make([]*api.Hook, len(sysHooks)) - for i, hook := range sysHooks { + + hooks := make([]*api.Hook, len(adminHooks)) + for i, hook := range adminHooks { h, err := webhook_service.ToHook(setting.AppURL+"/admin", hook) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) @@ -54,14 +55,20 @@ func ListHooks(ctx *context.APIContext) { ctx.JSON(http.StatusOK, hooks) } -// GetHook get an organization's hook by id +// get a system/default hook by id func GetHook(ctx *context.APIContext) { - // swagger:operation GET /admin/hooks/{id} admin adminGetHook + // swagger:operation GET /admin/hooks/{configType}/{id} admin adminGetHook // --- // summary: Get a hook // produces: // - application/json // parameters: + // - name: configType + // in: path + // description: whether the hook is system-wide or copied-to-each-new-repo + // type: string + // enum: [system, default] + // required: true // - name: id // in: path // description: id of the hook to get @@ -72,16 +79,19 @@ func GetHook(ctx *context.APIContext) { // "200": // "$ref": "#/responses/Hook" + isSystemWebhook := ctx.Params(":configType") == "system" + hookID := ctx.ParamsInt64(":id") - hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) + hook, err := webhook.GetAdminWebhook(ctx, hookID, isSystemWebhook) if err != nil { if errors.Is(err, util.ErrNotExist) { ctx.NotFound() } else { - ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + ctx.Error(http.StatusInternalServerError, "GetAdminWebhook", err) } return } + h, err := webhook_service.ToHook("/admin/", hook) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) @@ -90,9 +100,9 @@ func GetHook(ctx *context.APIContext) { ctx.JSON(http.StatusOK, h) } -// CreateHook create a hook for an organization +// create a system or default hook func CreateHook(ctx *context.APIContext) { - // swagger:operation POST /admin/hooks admin adminCreateHook + // swagger:operation POST /admin/hooks/{configType} admin adminCreateHook // --- // summary: Create a hook // consumes: @@ -100,6 +110,12 @@ func CreateHook(ctx *context.APIContext) { // produces: // - application/json // parameters: + // - name: configType + // in: path + // description: whether the hook is system-wide or copied-to-each-new-repo + // type: string + // enum: [system, default] + // required: true // - name: body // in: body // required: true @@ -109,14 +125,16 @@ func CreateHook(ctx *context.APIContext) { // "201": // "$ref": "#/responses/Hook" + isSystemWebhook := ctx.Params(":configType") == "system" + form := web.GetForm(ctx).(*api.CreateHookOption) - utils.AddSystemHook(ctx, form) + utils.AddAdminHook(ctx, form, isSystemWebhook) } -// EditHook modify a hook of a repository +// modify a system or default hook func EditHook(ctx *context.APIContext) { - // swagger:operation PATCH /admin/hooks/{id} admin adminEditHook + // swagger:operation PATCH /admin/hooks/{configType}/{id} admin adminEditHook // --- // summary: Update a hook // consumes: @@ -124,6 +142,12 @@ func EditHook(ctx *context.APIContext) { // produces: // - application/json // parameters: + // - name: configType + // in: path + // description: whether the hook is system-wide or copied-to-each-new-repo + // type: string + // enum: [system, default] + // required: true // - name: id // in: path // description: id of the hook to update @@ -138,21 +162,29 @@ func EditHook(ctx *context.APIContext) { // "200": // "$ref": "#/responses/Hook" + isSystemWebhook := ctx.Params(":configType") == "system" + form := web.GetForm(ctx).(*api.EditHookOption) // TODO in body params hookID := ctx.ParamsInt64(":id") - utils.EditSystemHook(ctx, form, hookID) + utils.EditAdminHook(ctx, form, hookID, isSystemWebhook) } -// DeleteHook delete a system hook +// delete a system or default hook func DeleteHook(ctx *context.APIContext) { - // swagger:operation DELETE /admin/hooks/{id} admin adminDeleteHook + // swagger:operation DELETE /admin/hooks/{configType}/{id} admin adminDeleteHook // --- // summary: Delete a hook // produces: // - application/json // parameters: + // - name: configType + // in: path + // description: whether the hook is system-wide or copied-to-each-new-repo + // type: string + // enum: [system, default] + // required: true // - name: id // in: path // description: id of the hook to delete @@ -163,12 +195,14 @@ func DeleteHook(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" + isSystemWebhook := ctx.Params(":configType") == "system" + hookID := ctx.ParamsInt64(":id") - if err := webhook.DeleteDefaultSystemWebhook(ctx, hookID); err != nil { + if err := webhook.DeleteAdminWebhook(ctx, hookID, isSystemWebhook); err != nil { if errors.Is(err, util.ErrNotExist) { ctx.NotFound() } else { - ctx.Error(http.StatusInternalServerError, "DeleteDefaultSystemWebhook", err) + ctx.Error(http.StatusInternalServerError, "DeleteAdminWebhook", err) } return } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 397eb105582b0..4e1addd663375 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1381,7 +1381,7 @@ func Routes() *web.Route { m.Post("/{username}/{reponame}", admin.AdoptRepository) m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) - m.Group("/hooks", func() { + m.Group("/hooks/{configType:system|default}", func() { m.Combo("").Get(admin.ListHooks). Post(bind(api.CreateHookOption{}), admin.CreateHook) m.Combo("/{id}").Get(admin.GetHook). diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index b62d20a18a617..03eb1ef512174 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -4,6 +4,7 @@ package utils import ( + "errors" "fmt" "net/http" "strings" @@ -100,9 +101,9 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) return true } -// AddSystemHook add a system hook -func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) { - hook, ok := addHook(ctx, form, 0, 0) +// add a system or default hook +func AddAdminHook(ctx *context.APIContext, form *api.CreateHookOption, isSystemWebhook bool) { + hook, ok := addHook(ctx, form, 0, 0, isSystemWebhook) if ok { h, err := webhook_service.ToHook(setting.AppSubURL+"/admin", hook) if err != nil { @@ -115,7 +116,7 @@ func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) { // AddOwnerHook adds a hook to an user or organization func AddOwnerHook(ctx *context.APIContext, owner *user_model.User, form *api.CreateHookOption) { - hook, ok := addHook(ctx, form, owner.ID, 0) + hook, ok := addHook(ctx, form, owner.ID, 0, false) if !ok { return } @@ -129,7 +130,7 @@ func AddOwnerHook(ctx *context.APIContext, owner *user_model.User, form *api.Cre // AddRepoHook add a hook to a repo. Writes to `ctx` accordingly func AddRepoHook(ctx *context.APIContext, form *api.CreateHookOption) { repo := ctx.Repo - hook, ok := addHook(ctx, form, 0, repo.Repository.ID) + hook, ok := addHook(ctx, form, 0, repo.Repository.ID, false) if !ok { return } @@ -159,9 +160,18 @@ func pullHook(events []string, event string) bool { return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true) } -// addHook add the hook specified by `form`, `ownerID` and `repoID`. If there is -// an error, write to `ctx` accordingly. Return (webhook, ok) -func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64) (*webhook.Webhook, bool) { +// addHook add the hook specified by `form`, `ownerID`, `repoID`, and `isSystemWebhook`. +// `isSystemWebhook` == true means it's a hook attached automatically to all repos +// `isSystemWebhook` == false && ownerID == 0 && repoID == 0 means it's a default hook, automatically copied to all new repos +// `ownerID` != 0 means it runs on all their repos +// `repoID` != 0 means it is an active hook, attached to that repo +// If there is an error, write to `ctx` accordingly. Return (webhook, ok) +func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64, isSystemWebhook bool) (*webhook.Webhook, bool) { + if isSystemWebhook && (ownerID != 0 || repoID != 0) { + ctx.Error(http.StatusInternalServerError, "addHook", fmt.Errorf("cannot create a hook with an owner or repo that is also a system hook")) + return nil, false + } + if !checkCreateHookOption(ctx, form) { return nil, false } @@ -170,12 +180,13 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI form.Events = []string{"push"} } w := &webhook.Webhook{ - OwnerID: ownerID, - RepoID: repoID, - URL: form.Config["url"], - ContentType: webhook.ToHookContentType(form.Config["content_type"]), - Secret: form.Config["secret"], - HTTPMethod: "POST", + OwnerID: ownerID, + RepoID: repoID, + URL: form.Config["url"], + ContentType: webhook.ToHookContentType(form.Config["content_type"]), + Secret: form.Config["secret"], + IsSystemWebhook: isSystemWebhook, + HTTPMethod: "POST", HookEvent: &webhook_module.HookEvent{ ChooseEvents: true, HookEvents: webhook_module.HookEvents{ @@ -246,22 +257,28 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI return w, true } -// EditSystemHook edit system webhook `w` according to `form`. Writes to `ctx` accordingly -func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) { - hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) +// EditAdminHook edits system/default webhook `w` according to `form`. Writes to `ctx` accordingly. +func EditAdminHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64, isSystemWebhook bool) { + hook, err := webhook.GetAdminWebhook(ctx, hookID, isSystemWebhook) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + if errors.Is(err, util.ErrNotExist) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetAdminWebhook", err) + } return } + if !editHook(ctx, form, hook) { - ctx.Error(http.StatusInternalServerError, "editHook", err) return } - updated, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID) + + updated, err := webhook.GetAdminWebhook(ctx, hookID, isSystemWebhook) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err) + ctx.Error(http.StatusInternalServerError, "GetAdminWebhook", err) return } + h, err := webhook_service.ToHook(setting.AppURL+"/admin", updated) if err != nil { ctx.Error(http.StatusInternalServerError, "convert.ToHook", err) diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index cd8cc29cdfbc0..5a0c273d6ca82 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -18,8 +18,8 @@ const ( tplAdminHooks base.TplName = "admin/hooks" ) -// DefaultOrSystemWebhooks renders both admin default and system webhook list pages -func DefaultOrSystemWebhooks(ctx *context.Context) { +// renders both admin default and system webhook list pages +func Webhooks(ctx *context.Context) { var err error ctx.Data["Title"] = ctx.Tr("admin.hooks") @@ -36,20 +36,20 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { sys["Title"] = ctx.Tr("admin.systemhooks") sys["Description"] = ctx.Tr("admin.systemhooks.desc") sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, util.OptionalBoolNone) - sys["BaseLink"] = setting.AppSubURL + "/admin/hooks" - sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks" + sys["BaseLink"] = setting.AppSubURL + "/admin/hooks/system" + sys["BaseLinkNew"] = setting.AppSubURL + "/admin/hooks/system" if err != nil { - ctx.ServerError("GetWebhooksAdmin", err) + ctx.ServerError("GetSystemWebhooks", err) return } def["Title"] = ctx.Tr("admin.defaulthooks") def["Description"] = ctx.Tr("admin.defaulthooks.desc") def["Webhooks"], err = webhook.GetDefaultWebhooks(ctx) - def["BaseLink"] = setting.AppSubURL + "/admin/hooks" - def["BaseLinkNew"] = setting.AppSubURL + "/admin/default-hooks" + def["BaseLink"] = setting.AppSubURL + "/admin/hooks/default" + def["BaseLinkNew"] = setting.AppSubURL + "/admin/hooks/default" if err != nil { - ctx.ServerError("GetWebhooksAdmin", err) + ctx.ServerError("GetDefaultWebhooks", err) return } @@ -59,9 +59,10 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { ctx.HTML(http.StatusOK, tplAdminHooks) } -// DeleteDefaultOrSystemWebhook handler to delete an admin-defined system or default webhook -func DeleteDefaultOrSystemWebhook(ctx *context.Context) { - if err := webhook.DeleteDefaultSystemWebhook(ctx, ctx.FormInt64("id")); err != nil { +// handler to delete an admin-defined system or default webhook +func DeleteWebhook(ctx *context.Context) { + isSystemWebhook := ctx.Params(":configType") == "system" + if err := webhook.DeleteAdminWebhook(ctx, ctx.FormInt64("id"), isSystemWebhook); err != nil { ctx.Flash.Error("DeleteDefaultWebhook: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index 5c4e1d47d09ad..5055c377edc45 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -61,9 +61,11 @@ type ownerRepoCtx struct { RepoID int64 IsAdmin bool IsSystemWebhook bool - Link string - LinkNew string - NewTemplate base.TplName + // Link is the page that list all hooks -- whether that's for the current repo, the org, or the site + Link string + // LinkNew and NewTemplate double as LinkEdit and EditTemplate + LinkNew string + NewTemplate base.TplName } // getOwnerRepoCtx determines whether this is a repo, owner, or admin (both default and system) context. @@ -98,9 +100,9 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) { if ctx.Data["PageIsAdmin"] == true { return &ownerRepoCtx{ IsAdmin: true, - IsSystemWebhook: ctx.Params(":configType") == "system-hooks", + IsSystemWebhook: ctx.Params(":configType") == "system", Link: path.Join(setting.AppSubURL, "/admin/hooks"), - LinkNew: path.Join(setting.AppSubURL, "/admin/", ctx.Params(":configType")), + LinkNew: path.Join(setting.AppSubURL, "/admin/hooks/", ctx.Params(":configType")), NewTemplate: tplAdminHookNew, }, nil } @@ -306,7 +308,7 @@ func editWebhook(ctx *context.Context, params webhookParams) { } ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success")) - ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID)) + ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.LinkNew, w.ID)) } // GiteaHooksNewPost response for creating Gitea webhook @@ -584,7 +586,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) { ctx.ServerError("getOwnerRepoCtx", err) return nil, nil } - ctx.Data["BaseLink"] = orCtx.Link + ctx.Data["BaseLink"] = orCtx.LinkNew var w *webhook.Webhook if orCtx.RepoID > 0 { @@ -592,7 +594,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) { } else if orCtx.OwnerID > 0 { w, err = webhook.GetWebhookByOwnerID(orCtx.OwnerID, ctx.ParamsInt64(":id")) } else if orCtx.IsAdmin { - w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id")) + w, err = webhook.GetAdminWebhook(ctx, ctx.ParamsInt64(":id"), orCtx.IsSystemWebhook) } if err != nil || w == nil { if webhook.IsErrWebhookNotExist(err) { @@ -718,7 +720,7 @@ func ReplayWebhook(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.settings.webhook.delivery.success")) - ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID)) + ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.LinkNew, w.ID)) } // DeleteWebhook delete a webhook diff --git a/routers/web/web.go b/routers/web/web.go index f857a36b04a8d..c2fef6a56404b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -601,19 +601,18 @@ func registerRoutes(m *web.Route) { }, packagesEnabled) m.Group("/hooks", func() { - m.Get("", admin.DefaultOrSystemWebhooks) - m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) - m.Group("/{id}", func() { - m.Get("", repo_setting.WebHooksEdit) - m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) + m.Get("", admin.Webhooks) + m.Group("/{configType:system|default}", func() { + m.Post("/delete", admin.DeleteWebhook) + m.Group("/{id}", func() { + m.Get("", repo_setting.WebHooksEdit) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) + }) + addWebhookAddRoutes() + addWebhookEditRoutes() }) - addWebhookEditRoutes() }, webhooksEnabled) - m.Group("/{configType:default-hooks|system-hooks}", func() { - addWebhookAddRoutes() - }) - m.Group("/auths", func() { m.Get("", admin.Authentications) m.Combo("/new").Get(admin.NewAuthSource).Post(web.Bind(forms.AuthenticationForm{}), admin.NewAuthSourcePost) diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl index 1bc9447110277..62c4530ff86b3 100644 --- a/templates/repo/settings/webhook/base_list.tmpl +++ b/templates/repo/settings/webhook/base_list.tmpl @@ -62,7 +62,7 @@ {{svg "octicon-dot-fill" 22}} {{.URL}} {{svg "octicon-pencil"}} - {{svg "octicon-trash"}} + {{svg "octicon-trash"}} {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 8cf5332bafc48..46438a0bda914 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -212,7 +212,7 @@ } } }, - "/admin/hooks": { + "/admin/hooks/{configType}": { "get": { "produces": [ "application/json" @@ -224,16 +224,15 @@ "operationId": "adminListHooks", "parameters": [ { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results", - "name": "limit", - "in": "query" + "enum": [ + "system", + "default" + ], + "type": "string", + "description": "whether the hook is system-wide or copied-to-each-new-repo", + "name": "configType", + "in": "path", + "required": true } ], "responses": { @@ -255,6 +254,17 @@ "summary": "Create a hook", "operationId": "adminCreateHook", "parameters": [ + { + "enum": [ + "system", + "default" + ], + "type": "string", + "description": "whether the hook is system-wide or copied-to-each-new-repo", + "name": "configType", + "in": "path", + "required": true + }, { "name": "body", "in": "body", @@ -271,7 +281,7 @@ } } }, - "/admin/hooks/{id}": { + "/admin/hooks/{configType}/{id}": { "get": { "produces": [ "application/json" @@ -282,6 +292,17 @@ "summary": "Get a hook", "operationId": "adminGetHook", "parameters": [ + { + "enum": [ + "system", + "default" + ], + "type": "string", + "description": "whether the hook is system-wide or copied-to-each-new-repo", + "name": "configType", + "in": "path", + "required": true + }, { "type": "integer", "format": "int64", @@ -307,6 +328,17 @@ "summary": "Delete a hook", "operationId": "adminDeleteHook", "parameters": [ + { + "enum": [ + "system", + "default" + ], + "type": "string", + "description": "whether the hook is system-wide or copied-to-each-new-repo", + "name": "configType", + "in": "path", + "required": true + }, { "type": "integer", "format": "int64", @@ -335,6 +367,17 @@ "summary": "Update a hook", "operationId": "adminEditHook", "parameters": [ + { + "enum": [ + "system", + "default" + ], + "type": "string", + "description": "whether the hook is system-wide or copied-to-each-new-repo", + "name": "configType", + "in": "path", + "required": true + }, { "type": "integer", "format": "int64",