forgejo/routers/web/repo/setting/webhook.go

564 lines
16 KiB
Go
Raw Normal View History

2015-12-05 21:24:13 +03:00
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2015-12-05 21:24:13 +03:00
package setting
2015-12-05 21:24:13 +03:00
import (
"errors"
"fmt"
"net/http"
"net/url"
"path"
2015-12-05 21:24:13 +03:00
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2024-03-21 15:23:27 +03:00
"code.gitea.io/gitea/modules/web/middleware"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
webhook_service "code.gitea.io/gitea/services/webhook"
2024-03-21 15:23:27 +03:00
"gitea.com/go-chi/binding"
2015-12-05 21:24:13 +03:00
)
const (
tplHooks base.TplName = "repo/settings/webhook/base"
tplHookNew base.TplName = "repo/settings/webhook/new"
tplOrgHookNew base.TplName = "org/settings/hook_new"
tplUserHookNew base.TplName = "user/settings/hook_new"
tplAdminHookNew base.TplName = "admin/hook_new"
2015-12-05 21:24:13 +03:00
)
2016-11-24 10:04:31 +03:00
// Webhooks render web hooks list page
2016-03-11 19:56:52 +03:00
func Webhooks(ctx *context.Context) {
2015-12-05 21:24:13 +03:00
ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
[BRANDING] add the forgejo webhook type & update webhook docs URLs templates/swagger/v1_json.tmpl updated with `make generate-swagger` (cherry picked from commit 88899c492efeedd138ba088a36b9c0bc733ead7b) (cherry picked from commit 7171bd9617c32c4911e3bdbc23c02a19e80d2465) (cherry picked from commit 1a742446c17aef9ca62fe75bfc0a388d40138154) (cherry picked from commit d7c189d7b2f9fea299a31adf068db969920ae39d) Conflicts: routers/web/web.go (cherry picked from commit cbdea868e41fb38ca491f8b449c3e525ec82d6b9) (cherry picked from commit 6cd150483b06e17aee023c0afd01a3f2460b3415) (cherry picked from commit 47246da8d3f50a02d11b77b3d402618b144aa720) (cherry picked from commit f2aa0e6b769d432e627798bcf294b04b7d253213) (cherry picked from commit 5a4fc69a16de8d6199ea24198299297ef7a3587b) (cherry picked from commit 48e444ca09c22f930514a01846b0c8bc3cef35ab) (cherry picked from commit 888e53781175d8d977f66d78991bd66563fcddfb) (cherry picked from commit 5121f493c99f19d8050aa09224ac3532b4100ec7) (cherry picked from commit 9394e55fdf80bf3d7bf8b2aba561ad44a84e3913) (cherry picked from commit 3a2ce51768de65892e3ec73596e3862354c9502e) (cherry picked from commit 719ead3a651f12afbb59c856914b0085e5cee157) (cherry picked from commit 83e6f82e2aea619a3cd502e133773d33c0e60133) (cherry picked from commit 494a429b21c6234be38b9e3db0f930fbb8118205) (cherry picked from commit 4d775db6b41f731e956cc6bb9217ef349b4a3635) (cherry picked from commit b68f777dc2822ec5c4e30186675cc82daec092a9) (cherry picked from commit 5b934023fa58820f27c349c26f2a1ce89aee6795) (cherry picked from commit 3b1ed8b16c73374cd5b6339f5315229dc82488da) (cherry picked from commit 6bc4a46c9fc6472e1c4bf0bb20dea6867f1b392f) (cherry picked from commit 8064bb24a3c752a86271f154ad4d0c4763e07295) Conflicts: templates/admin/hook_new.tmpl templates/org/settings/hook_new.tmpl templates/repo/settings/webhook/base_list.tmpl templates/repo/settings/webhook/new.tmpl templates/user/settings/hook_new.tmpl https://codeberg.org/forgejo/forgejo/pulls/1181 (cherry picked from commit 55f5588a9150d8912c0f8342495f858e4e1e2959) (cherry picked from commit a428bc06b353e7b678acc6a1ec2576608f1a5d9a) (cherry picked from commit d2186eceb960773d037756c991f50d177fd63954) (cherry picked from commit b4e126e9afd1c51b214e36a585bb454dac32862f) (cherry picked from commit e4c7a92c2d7142cac74cf05a2b1fe3f0bd0c7373) (cherry picked from commit a7165c8146a604b6ec5f8ee4db4c926fef180857) Conflicts: templates/admin/hook_new.tmpl templates/repo/settings/webhook/new.tmpl https://codeberg.org/forgejo/forgejo/pulls/1420 [BRANDING] add the forgejo webhook type & update webhook docs URLs (squash) gitea logo for gitea webhooks Refs: https://codeberg.org/forgejo/forgejo/issues/1367 (cherry picked from commit 2d8c1b93734acec918729e27c5613a8f9fb41ceb) (cherry picked from commit 9135a5e1dbcff07e9fff4c1001fcac8c05e0c82b) (cherry picked from commit 270f4020b0714ac553498fc986457b0c627d85a6) (cherry picked from commit 33e5e2f0d898639bae05225437aeb4afa2678360) (cherry picked from commit 7af8bcf4791ade3ca80d449629bb2eadd0ad25ba) Conflicts: tests/integration/links_test.go https://codeberg.org/forgejo/forgejo/pulls/1548 (cherry picked from commit ce2a434dcf9ad23337aaa0cc30dc069bbc79744c) (cherry picked from commit c2fa42b4fda37db8d5d4efc11834546d5de3d6ad) (cherry picked from commit 3858c8d4db2ceaf79bd5ebed54382cbdd6491a5b) (cherry picked from commit ebb0ae49208adeb5da4c6b442990db1b1dbe91d0) (cherry picked from commit 93d354c418e6da1971dd7f1749cb700627f1f6d2) (cherry picked from commit d5b08a2a4be2a48925dc089f5ec312188c60036c) (cherry picked from commit e5e5caffaa69c2f2be300cc97ec500a993ac6b3b) (cherry picked from commit 974d4057000f6989eb4b3440271c78dc05927312) (cherry picked from commit e654fbccb1ddfd9e494ce4560e5ac1620d85f7c6) (cherry picked from commit 85515977fb2fb6f6ae13f7b011bfe9f9c6d0425f) [BRANDING] add the forgejo webhook type & update webhook docs URLs (squash) ctx.Locale (cherry picked from commit e192647bc0f1d19b144e5ab1b286e11b2466eecd) (cherry picked from commit 95fb1b724201b4d0dc1ef0ffb2745b2977c73286) (cherry picked from commit ea7bd8022fc780b83de4a9ef5595cb0067e6e9d8) (cherry picked from commit bf8465ccd07a0be493d2faf21e1e2c62766513a4) (cherry picked from commit e1baa0a3007e2cc9a569da7d1e48785744d5cde6) (cherry picked from commit 01b4f4c51428fad992e95615e3d55be54b4f84f9) (cherry picked from commit da330b1e51916bb3b55167b5fefe86787a470e1f) (cherry picked from commit 7b26c27c209c7ee5dbb45b74bd253c1bc713f5b5) (cherry picked from commit 0c781e6f6d411335b4d22cd6a691b409379eba62) (cherry picked from commit 0f663a8414e248f67d18896863d34f03abd9738d) (cherry picked from commit 3297fd4f15ee7885d7b88c79e1ce70832cc60f78) (cherry picked from commit 2f4ef403cb9e92c0a0785b860747283bd754f826)
2023-01-14 12:07:01 +03:00
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://forgejo.org/docs/latest/user/webhooks/")
2015-12-05 21:24:13 +03:00
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
2015-12-05 21:24:13 +03:00
if err != nil {
ctx.ServerError("GetWebhooksByRepoID", err)
2015-12-05 21:24:13 +03:00
return
}
ctx.Data["Webhooks"] = ws
ctx.HTML(http.StatusOK, tplHooks)
2015-12-05 21:24:13 +03:00
}
type ownerRepoCtx struct {
OwnerID int64
RepoID int64
IsAdmin bool
IsSystemWebhook bool
Link string
LinkNew string
NewTemplate base.TplName
2015-12-05 21:24:13 +03:00
}
// getOwnerRepoCtx determines whether this is a repo, owner, or admin (both default and system) context.
func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
Move secrets and runners settings to actions settings (#24200) This PR moves the secrets and runners settings to actions settings on all settings(repo,org,user,admin) levels. After this PR, if [ENABLED](https://github.com/go-gitea/gitea/blob/5e7543fcf441afb30aba6188edac754ef32b9ac3/custom/conf/app.example.ini#L2604) inside `app.ini` under `[actions]` is set to `false`, the "Actions" tab (including runners management and secrets management) will not be shown. After, the settings under actions settings for each level: 1. Admin Level "Runners Management" <img width="1437" alt="Screen Shot 2023-04-26 at 14 34 20" src="https://user-images.githubusercontent.com/17645053/234489731-15822d21-38e1-4560-8bbe-69f122376abc.png"> 2. User Level "Secrets Management" <img width="1427" alt="Screen Shot 2023-04-26 at 14 34 30" src="https://user-images.githubusercontent.com/17645053/234489795-68c9c0cb-24f8-4f09-95c6-458ab914c313.png"> 3. Repo and Organization Levels "Runners Management" and "Secrets Management" Org: <img width="1437" alt="Screen Shot 2023-04-26 at 14 35 07" src="https://user-images.githubusercontent.com/17645053/234489996-f3af5ebb-d354-46ca-9087-a0b586845281.png"> <img width="1433" alt="Screen Shot 2023-04-26 at 14 35 14" src="https://user-images.githubusercontent.com/17645053/234490004-3abf8fed-81fd-4ce2-837a-935dade1793d.png"> Repo: <img width="1419" alt="Screen Shot 2023-04-26 at 14 34 50" src="https://user-images.githubusercontent.com/17645053/234489904-80c11038-4b58-462c-9d0b-8b7cf70bc2b3.png"> <img width="1430" alt="Screen Shot 2023-04-26 at 14 34 57" src="https://user-images.githubusercontent.com/17645053/234489918-4e8d1fe2-9bcd-4d8a-96c1-238a8088d92e.png"> It also finished these tasks : - [x] rename routers function "runners" to "actions", and refactor related file names - [x] check and modify part of the runners related functions to match their name - [x] Fix backend check caused by fmt check --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-04-28 03:08:47 +03:00
if ctx.Data["PageIsRepoSettings"] == true {
return &ownerRepoCtx{
2015-12-05 21:24:13 +03:00
RepoID: ctx.Repo.Repository.ID,
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
NewTemplate: tplHookNew,
2015-12-05 21:24:13 +03:00
}, nil
}
Move secrets and runners settings to actions settings (#24200) This PR moves the secrets and runners settings to actions settings on all settings(repo,org,user,admin) levels. After this PR, if [ENABLED](https://github.com/go-gitea/gitea/blob/5e7543fcf441afb30aba6188edac754ef32b9ac3/custom/conf/app.example.ini#L2604) inside `app.ini` under `[actions]` is set to `false`, the "Actions" tab (including runners management and secrets management) will not be shown. After, the settings under actions settings for each level: 1. Admin Level "Runners Management" <img width="1437" alt="Screen Shot 2023-04-26 at 14 34 20" src="https://user-images.githubusercontent.com/17645053/234489731-15822d21-38e1-4560-8bbe-69f122376abc.png"> 2. User Level "Secrets Management" <img width="1427" alt="Screen Shot 2023-04-26 at 14 34 30" src="https://user-images.githubusercontent.com/17645053/234489795-68c9c0cb-24f8-4f09-95c6-458ab914c313.png"> 3. Repo and Organization Levels "Runners Management" and "Secrets Management" Org: <img width="1437" alt="Screen Shot 2023-04-26 at 14 35 07" src="https://user-images.githubusercontent.com/17645053/234489996-f3af5ebb-d354-46ca-9087-a0b586845281.png"> <img width="1433" alt="Screen Shot 2023-04-26 at 14 35 14" src="https://user-images.githubusercontent.com/17645053/234490004-3abf8fed-81fd-4ce2-837a-935dade1793d.png"> Repo: <img width="1419" alt="Screen Shot 2023-04-26 at 14 34 50" src="https://user-images.githubusercontent.com/17645053/234489904-80c11038-4b58-462c-9d0b-8b7cf70bc2b3.png"> <img width="1430" alt="Screen Shot 2023-04-26 at 14 34 57" src="https://user-images.githubusercontent.com/17645053/234489918-4e8d1fe2-9bcd-4d8a-96c1-238a8088d92e.png"> It also finished these tasks : - [x] rename routers function "runners" to "actions", and refactor related file names - [x] check and modify part of the runners related functions to match their name - [x] Fix backend check caused by fmt check --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-04-28 03:08:47 +03:00
if ctx.Data["PageIsOrgSettings"] == true {
return &ownerRepoCtx{
OwnerID: ctx.ContextUser.ID,
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
NewTemplate: tplOrgHookNew,
2015-12-05 21:24:13 +03:00
}, nil
}
Move secrets and runners settings to actions settings (#24200) This PR moves the secrets and runners settings to actions settings on all settings(repo,org,user,admin) levels. After this PR, if [ENABLED](https://github.com/go-gitea/gitea/blob/5e7543fcf441afb30aba6188edac754ef32b9ac3/custom/conf/app.example.ini#L2604) inside `app.ini` under `[actions]` is set to `false`, the "Actions" tab (including runners management and secrets management) will not be shown. After, the settings under actions settings for each level: 1. Admin Level "Runners Management" <img width="1437" alt="Screen Shot 2023-04-26 at 14 34 20" src="https://user-images.githubusercontent.com/17645053/234489731-15822d21-38e1-4560-8bbe-69f122376abc.png"> 2. User Level "Secrets Management" <img width="1427" alt="Screen Shot 2023-04-26 at 14 34 30" src="https://user-images.githubusercontent.com/17645053/234489795-68c9c0cb-24f8-4f09-95c6-458ab914c313.png"> 3. Repo and Organization Levels "Runners Management" and "Secrets Management" Org: <img width="1437" alt="Screen Shot 2023-04-26 at 14 35 07" src="https://user-images.githubusercontent.com/17645053/234489996-f3af5ebb-d354-46ca-9087-a0b586845281.png"> <img width="1433" alt="Screen Shot 2023-04-26 at 14 35 14" src="https://user-images.githubusercontent.com/17645053/234490004-3abf8fed-81fd-4ce2-837a-935dade1793d.png"> Repo: <img width="1419" alt="Screen Shot 2023-04-26 at 14 34 50" src="https://user-images.githubusercontent.com/17645053/234489904-80c11038-4b58-462c-9d0b-8b7cf70bc2b3.png"> <img width="1430" alt="Screen Shot 2023-04-26 at 14 34 57" src="https://user-images.githubusercontent.com/17645053/234489918-4e8d1fe2-9bcd-4d8a-96c1-238a8088d92e.png"> It also finished these tasks : - [x] rename routers function "runners" to "actions", and refactor related file names - [x] check and modify part of the runners related functions to match their name - [x] Fix backend check caused by fmt check --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-04-28 03:08:47 +03:00
if ctx.Data["PageIsUserSettings"] == true {
return &ownerRepoCtx{
OwnerID: ctx.Doer.ID,
Link: path.Join(setting.AppSubURL, "/user/settings/hooks"),
LinkNew: path.Join(setting.AppSubURL, "/user/settings/hooks"),
NewTemplate: tplUserHookNew,
}, nil
}
Move secrets and runners settings to actions settings (#24200) This PR moves the secrets and runners settings to actions settings on all settings(repo,org,user,admin) levels. After this PR, if [ENABLED](https://github.com/go-gitea/gitea/blob/5e7543fcf441afb30aba6188edac754ef32b9ac3/custom/conf/app.example.ini#L2604) inside `app.ini` under `[actions]` is set to `false`, the "Actions" tab (including runners management and secrets management) will not be shown. After, the settings under actions settings for each level: 1. Admin Level "Runners Management" <img width="1437" alt="Screen Shot 2023-04-26 at 14 34 20" src="https://user-images.githubusercontent.com/17645053/234489731-15822d21-38e1-4560-8bbe-69f122376abc.png"> 2. User Level "Secrets Management" <img width="1427" alt="Screen Shot 2023-04-26 at 14 34 30" src="https://user-images.githubusercontent.com/17645053/234489795-68c9c0cb-24f8-4f09-95c6-458ab914c313.png"> 3. Repo and Organization Levels "Runners Management" and "Secrets Management" Org: <img width="1437" alt="Screen Shot 2023-04-26 at 14 35 07" src="https://user-images.githubusercontent.com/17645053/234489996-f3af5ebb-d354-46ca-9087-a0b586845281.png"> <img width="1433" alt="Screen Shot 2023-04-26 at 14 35 14" src="https://user-images.githubusercontent.com/17645053/234490004-3abf8fed-81fd-4ce2-837a-935dade1793d.png"> Repo: <img width="1419" alt="Screen Shot 2023-04-26 at 14 34 50" src="https://user-images.githubusercontent.com/17645053/234489904-80c11038-4b58-462c-9d0b-8b7cf70bc2b3.png"> <img width="1430" alt="Screen Shot 2023-04-26 at 14 34 57" src="https://user-images.githubusercontent.com/17645053/234489918-4e8d1fe2-9bcd-4d8a-96c1-238a8088d92e.png"> It also finished these tasks : - [x] rename routers function "runners" to "actions", and refactor related file names - [x] check and modify part of the runners related functions to match their name - [x] Fix backend check caused by fmt check --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-04-28 03:08:47 +03:00
if ctx.Data["PageIsAdmin"] == true {
return &ownerRepoCtx{
IsAdmin: true,
IsSystemWebhook: ctx.Params(":configType") == "system-hooks",
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
LinkNew: path.Join(setting.AppSubURL, "/admin/", ctx.Params(":configType")),
NewTemplate: tplAdminHookNew,
}, nil
}
return nil, errors.New("unable to set OwnerRepo context")
2015-12-05 21:24:13 +03:00
}
2016-03-11 19:56:52 +03:00
func checkHookType(ctx *context.Context) string {
2015-12-05 21:24:13 +03:00
hookType := strings.ToLower(ctx.Params(":type"))
if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
ctx.NotFound("checkHookType", nil)
2015-12-05 21:24:13 +03:00
return ""
}
return hookType
}
2016-11-24 10:04:31 +03:00
// WebhooksNew render creating webhook page
2016-03-11 19:56:52 +03:00
func WebhooksNew(ctx *context.Context) {
2015-12-05 21:24:13 +03:00
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
2015-12-05 21:24:13 +03:00
orCtx, err := getOwnerRepoCtx(ctx)
2015-12-05 21:24:13 +03:00
if err != nil {
ctx.ServerError("getOwnerRepoCtx", err)
2015-12-05 21:24:13 +03:00
return
}
if orCtx.IsAdmin && orCtx.IsSystemWebhook {
ctx.Data["PageIsAdminSystemHooks"] = true
ctx.Data["PageIsAdminSystemHooksNew"] = true
} else if orCtx.IsAdmin {
ctx.Data["PageIsAdminDefaultHooks"] = true
ctx.Data["PageIsAdminDefaultHooksNew"] = true
} else {
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
}
hookType := checkHookType(ctx)
ctx.Data["HookType"] = hookType
2015-12-05 21:24:13 +03:00
if ctx.Written() {
return
}
if hookType == "discord" {
ctx.Data["DiscordHook"] = map[string]any{
"Username": "Gitea",
}
}
ctx.Data["BaseLink"] = orCtx.LinkNew
ctx.Data["BaseLinkNew"] = orCtx.LinkNew
2015-12-05 21:24:13 +03:00
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
2015-12-05 21:24:13 +03:00
}
// ParseHookEvent convert web form content to webhook.HookEvent
func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
return &webhook_module.HookEvent{
2015-12-05 21:24:13 +03:00
PushOnly: form.PushOnly(),
SendEverything: form.SendEverything(),
ChooseEvents: form.ChooseEvents(),
HookEvents: webhook_module.HookEvents{
Create: form.Create,
Delete: form.Delete,
Fork: form.Fork,
Issues: form.Issues,
IssueAssign: form.IssueAssign,
IssueLabel: form.IssueLabel,
IssueMilestone: form.IssueMilestone,
IssueComment: form.IssueComment,
Release: form.Release,
Push: form.Push,
PullRequest: form.PullRequest,
PullRequestAssign: form.PullRequestAssign,
PullRequestLabel: form.PullRequestLabel,
PullRequestMilestone: form.PullRequestMilestone,
PullRequestComment: form.PullRequestComment,
PullRequestReview: form.PullRequestReview,
PullRequestSync: form.PullRequestSync,
PullRequestReviewRequest: form.PullRequestReviewRequest,
Wiki: form.Wiki,
Repository: form.Repository,
Package: form.Package,
2015-12-05 21:24:13 +03:00
},
BranchFilter: form.BranchFilter,
2015-12-05 21:24:13 +03:00
}
}
2022-08-23 09:52:35 +03:00
type webhookParams struct {
// Type should be imported from webhook package (webhook.XXX)
Type string
URL string
ContentType webhook.HookContentType
Secret string
HTTPMethod string
WebhookForm forms.WebhookForm
Meta any
}
2024-03-21 15:23:27 +03:00
func WebhookCreate(ctx *context.Context) {
typ := ctx.Params(":type")
handler := webhook_service.GetWebhookHandler(typ)
if handler == nil {
ctx.NotFound("GetWebhookHandler", nil)
return
}
fields := handler.FormFields(func(form any) {
errs := binding.Bind(ctx.Req, form)
middleware.Validate(errs, ctx.Data, form, ctx.Locale) // error will be checked later in ctx.HasError
})
createWebhook(ctx, webhookParams{
Type: typ,
URL: fields.URL,
ContentType: fields.ContentType,
Secret: fields.Secret,
HTTPMethod: fields.HTTPMethod,
WebhookForm: fields.WebhookForm,
Meta: fields.Metadata,
})
}
2022-08-23 09:52:35 +03:00
func createWebhook(ctx *context.Context, params webhookParams) {
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
ctx.Data["HookType"] = params.Type
orCtx, err := getOwnerRepoCtx(ctx)
if err != nil {
ctx.ServerError("getOwnerRepoCtx", err)
return
}
ctx.Data["BaseLink"] = orCtx.LinkNew
ctx.Data["BaseLinkNew"] = orCtx.LinkNew
if ctx.HasError() {
ctx.HTML(http.StatusUnprocessableEntity, orCtx.NewTemplate)
return
}
var meta []byte
if params.Meta != nil {
meta, err = json.Marshal(params.Meta)
if err != nil {
ctx.ServerError("Marshal", err)
return
}
}
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: params.URL,
HTTPMethod: params.HTTPMethod,
ContentType: params.ContentType,
Secret: params.Secret,
HookEvent: ParseHookEvent(params.WebhookForm),
IsActive: params.WebhookForm.Active,
Type: params.Type,
Meta: string(meta),
OwnerID: orCtx.OwnerID,
IsSystemWebhook: orCtx.IsSystemWebhook,
}
Add Webhook authorization header (#20926) _This is a different approach to #20267, I took the liberty of adapting some parts, see below_ ## Context In some cases, a weebhook endpoint requires some kind of authentication. The usual way is by sending a static `Authorization` header, with a given token. For instance: - Matrix expects a `Bearer <token>` (already implemented, by storing the header cleartext in the metadata - which is buggy on retry #19872) - TeamCity #18667 - Gitea instances #20267 - SourceHut https://man.sr.ht/graphql.md#authentication-strategies (this is my actual personal need :) ## Proposed solution Add a dedicated encrypt column to the webhook table (instead of storing it as meta as proposed in #20267), so that it gets available for all present and future hook types (especially the custom ones #19307). This would also solve the buggy matrix retry #19872. As a first step, I would recommend focusing on the backend logic and improve the frontend at a later stage. For now the UI is a simple `Authorization` field (which could be later customized with `Bearer` and `Basic` switches): ![2022-08-23-142911](https://user-images.githubusercontent.com/3864879/186162483-5b721504-eef5-4932-812e-eb96a68494cc.png) The header name is hard-coded, since I couldn't fine any usecase justifying otherwise. ## Questions - What do you think of this approach? @justusbunsi @Gusted @silverwind - ~~How are the migrations generated? Do I have to manually create a new file, or is there a command for that?~~ - ~~I started adding it to the API: should I complete it or should I drop it? (I don't know how much the API is actually used)~~ ## Done as well: - add a migration for the existing matrix webhooks and remove the `Authorization` logic there _Closes #19872_ Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Gusted <williamzijl7@hotmail.com> Co-authored-by: delvh <dev.lh@web.de>
2022-11-03 21:23:20 +03:00
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
if err != nil {
ctx.ServerError("SetHeaderAuthorization", err)
return
}
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := webhook.CreateWebhook(ctx, w); err != nil {
ctx.ServerError("CreateWebhook", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link)
}
2024-03-21 15:23:27 +03:00
func WebhookUpdate(ctx *context.Context) {
typ := ctx.Params(":type")
handler := webhook_service.GetWebhookHandler(typ)
if handler == nil {
ctx.NotFound("GetWebhookHandler", nil)
return
}
fields := handler.FormFields(func(form any) {
errs := binding.Bind(ctx.Req, form)
middleware.Validate(errs, ctx.Data, form, ctx.Locale) // error will be checked later in ctx.HasError
})
editWebhook(ctx, webhookParams{
Type: typ,
URL: fields.URL,
ContentType: fields.ContentType,
Secret: fields.Secret,
HTTPMethod: fields.HTTPMethod,
WebhookForm: fields.WebhookForm,
Meta: fields.Metadata,
})
}
2022-08-23 09:52:35 +03:00
func editWebhook(ctx *context.Context, params webhookParams) {
ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksEdit"] = true
orCtx, w := checkWebhook(ctx)
if ctx.Written() {
return
}
ctx.Data["Webhook"] = w
if ctx.HasError() {
ctx.HTML(http.StatusUnprocessableEntity, orCtx.NewTemplate)
2022-08-23 09:52:35 +03:00
return
}
var meta []byte
var err error
if params.Meta != nil {
meta, err = json.Marshal(params.Meta)
if err != nil {
ctx.ServerError("Marshal", err)
return
}
}
w.URL = params.URL
w.ContentType = params.ContentType
w.Secret = params.Secret
w.HookEvent = ParseHookEvent(params.WebhookForm)
w.IsActive = params.WebhookForm.Active
w.HTTPMethod = params.HTTPMethod
w.Meta = string(meta)
Add Webhook authorization header (#20926) _This is a different approach to #20267, I took the liberty of adapting some parts, see below_ ## Context In some cases, a weebhook endpoint requires some kind of authentication. The usual way is by sending a static `Authorization` header, with a given token. For instance: - Matrix expects a `Bearer <token>` (already implemented, by storing the header cleartext in the metadata - which is buggy on retry #19872) - TeamCity #18667 - Gitea instances #20267 - SourceHut https://man.sr.ht/graphql.md#authentication-strategies (this is my actual personal need :) ## Proposed solution Add a dedicated encrypt column to the webhook table (instead of storing it as meta as proposed in #20267), so that it gets available for all present and future hook types (especially the custom ones #19307). This would also solve the buggy matrix retry #19872. As a first step, I would recommend focusing on the backend logic and improve the frontend at a later stage. For now the UI is a simple `Authorization` field (which could be later customized with `Bearer` and `Basic` switches): ![2022-08-23-142911](https://user-images.githubusercontent.com/3864879/186162483-5b721504-eef5-4932-812e-eb96a68494cc.png) The header name is hard-coded, since I couldn't fine any usecase justifying otherwise. ## Questions - What do you think of this approach? @justusbunsi @Gusted @silverwind - ~~How are the migrations generated? Do I have to manually create a new file, or is there a command for that?~~ - ~~I started adding it to the API: should I complete it or should I drop it? (I don't know how much the API is actually used)~~ ## Done as well: - add a migration for the existing matrix webhooks and remove the `Authorization` logic there _Closes #19872_ Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Gusted <williamzijl7@hotmail.com> Co-authored-by: delvh <dev.lh@web.de>
2022-11-03 21:23:20 +03:00
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
if err != nil {
ctx.ServerError("SetHeaderAuthorization", err)
return
}
2022-08-23 09:52:35 +03:00
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := webhook.UpdateWebhook(ctx, w); err != nil {
2022-08-23 09:52:35 +03:00
ctx.ServerError("UpdateWebhook", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}
// GogsHooksNewPost response for creating Gogs webhook
func GogsHooksNewPost(ctx *context.Context) {
2022-08-23 09:52:35 +03:00
createWebhook(ctx, gogsHookParams(ctx))
}
// GogsHooksEditPost response for editing Gogs webhook
func GogsHooksEditPost(ctx *context.Context) {
editWebhook(ctx, gogsHookParams(ctx))
}
func gogsHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewGogshookForm)
2015-12-05 21:24:13 +03:00
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
2015-12-05 21:24:13 +03:00
}
2022-08-23 09:52:35 +03:00
return webhookParams{
Type: webhook_module.GOGS,
URL: form.PayloadURL,
ContentType: contentType,
Secret: form.Secret,
WebhookForm: form.WebhookForm,
2022-08-23 09:52:35 +03:00
}
2015-12-05 21:24:13 +03:00
}
2022-08-23 09:52:35 +03:00
// PackagistHooksNewPost response for creating Packagist webhook
func PackagistHooksNewPost(ctx *context.Context) {
2022-08-23 09:52:35 +03:00
createWebhook(ctx, packagistHookParams(ctx))
}
// PackagistHooksEditPost response for editing Packagist webhook
func PackagistHooksEditPost(ctx *context.Context) {
editWebhook(ctx, packagistHookParams(ctx))
}
func packagistHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
2022-08-23 09:52:35 +03:00
return webhookParams{
Type: webhook_module.PACKAGIST,
URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.PackagistMeta{
Username: form.Username,
APIToken: form.APIToken,
PackageURL: form.PackageURL,
},
2022-08-23 09:52:35 +03:00
}
}
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
orCtx, err := getOwnerRepoCtx(ctx)
2015-12-05 21:24:13 +03:00
if err != nil {
ctx.ServerError("getOwnerRepoCtx", err)
2015-12-05 21:24:13 +03:00
return nil, nil
}
ctx.Data["BaseLink"] = orCtx.Link
ctx.Data["BaseLinkNew"] = orCtx.LinkNew
2015-12-05 21:24:13 +03:00
var w *webhook.Webhook
if orCtx.RepoID > 0 {
w, err = webhook.GetWebhookByRepoID(ctx, orCtx.RepoID, ctx.ParamsInt64(":id"))
} else if orCtx.OwnerID > 0 {
w, err = webhook.GetWebhookByOwnerID(ctx, orCtx.OwnerID, ctx.ParamsInt64(":id"))
} else if orCtx.IsAdmin {
w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id"))
}
if err != nil || w == nil {
if webhook.IsErrWebhookNotExist(err) {
ctx.NotFound("GetWebhookByID", nil)
2015-12-05 21:24:13 +03:00
} else {
ctx.ServerError("GetWebhookByID", err)
2015-12-05 21:24:13 +03:00
}
return nil, nil
}
ctx.Data["HookType"] = w.Type
2024-03-20 17:44:01 +03:00
if handler := webhook_service.GetWebhookHandler(w.Type); handler != nil {
ctx.Data["HookMetadata"] = handler.Metadata(w)
2015-12-05 21:24:13 +03:00
}
ctx.Data["History"], err = w.History(ctx, 1)
2015-12-05 21:24:13 +03:00
if err != nil {
ctx.ServerError("History", err)
2015-12-05 21:24:13 +03:00
}
return orCtx, w
}
2016-11-24 10:04:31 +03:00
// WebHooksEdit render editing web hook page
2016-03-11 19:56:52 +03:00
func WebHooksEdit(ctx *context.Context) {
2015-12-05 21:24:13 +03:00
ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksEdit"] = true
orCtx, w := checkWebhook(ctx)
if ctx.Written() {
return
}
ctx.Data["Webhook"] = w
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
2015-12-05 21:24:13 +03:00
}
2016-11-24 10:04:31 +03:00
// TestWebhook test if web hook is work fine
2016-03-11 19:56:52 +03:00
func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id")
w, err := webhook.GetWebhookByRepoID(ctx, ctx.Repo.Repository.ID, hookID)
if err != nil {
ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
ctx.Status(http.StatusInternalServerError)
return
}
// Grab latest commit or fake one if it's empty repository.
commit := ctx.Repo.Commit
if commit == nil {
ghost := user_model.NewGhostUser()
objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName)
commit = &git.Commit{
ID: objectFormat.EmptyObjectID(),
Author: ghost.NewGitSig(),
Committer: ghost.NewGitSig(),
CommitMessage: "This is a fake commit",
}
}
Add context cache as a request level cache (#22294) To avoid duplicated load of the same data in an HTTP request, we can set a context cache to do that. i.e. Some pages may load a user from a database with the same id in different areas on the same page. But the code is hidden in two different deep logic. How should we share the user? As a result of this PR, now if both entry functions accept `context.Context` as the first parameter and we just need to refactor `GetUserByID` to reuse the user from the context cache. Then it will not be loaded twice on an HTTP request. But of course, sometimes we would like to reload an object from the database, that's why `RemoveContextData` is also exposed. The core context cache is here. It defines a new context ```go type cacheContext struct { ctx context.Context data map[any]map[any]any lock sync.RWMutex } var cacheContextKey = struct{}{} func WithCacheContext(ctx context.Context) context.Context { return context.WithValue(ctx, cacheContextKey, &cacheContext{ ctx: ctx, data: make(map[any]map[any]any), }) } ``` Then you can use the below 4 methods to read/write/del the data within the same context. ```go func GetContextData(ctx context.Context, tp, key any) any func SetContextData(ctx context.Context, tp, key, value any) func RemoveContextData(ctx context.Context, tp, key any) func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) ``` Then let's take a look at how `system.GetString` implement it. ```go func GetSetting(ctx context.Context, key string) (string, error) { return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) { return cache.GetString(genSettingCacheKey(key), func() (string, error) { res, err := GetSettingNoCache(ctx, key) if err != nil { return "", err } return res.SettingValue, nil }) }) } ``` First, it will check if context data include the setting object with the key. If not, it will query from the global cache which may be memory or a Redis cache. If not, it will get the object from the database. In the end, if the object gets from the global cache or database, it will be set into the context cache. An object stored in the context cache will only be destroyed after the context disappeared.
2023-02-15 16:37:34 +03:00
apiUser := convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone)
apiCommit := &api.PayloadCommit{
ID: commit.ID.String(),
Message: commit.Message(),
URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
Author: &api.PayloadUser{
Name: commit.Author.Name,
Email: commit.Author.Email,
},
Committer: &api.PayloadUser{
Name: commit.Committer.Name,
Email: commit.Committer.Email,
2015-12-05 21:24:13 +03:00
},
}
commitID := commit.ID.String()
p := &api.PushPayload{
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
Before: commitID,
After: commitID,
CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
Commits: []*api.PayloadCommit{apiCommit},
TotalCommits: 1,
HeadCommit: apiCommit,
Repo: convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeNone}),
Pusher: apiUser,
Sender: apiUser,
2015-12-05 21:24:13 +03:00
}
if err := webhook_service.PrepareWebhook(ctx, w, webhook_module.HookEventPush, p); err != nil {
ctx.Flash.Error("PrepareWebhook: " + err.Error())
ctx.Status(http.StatusInternalServerError)
2015-12-05 21:24:13 +03:00
} else {
2022-01-06 00:00:20 +03:00
ctx.Flash.Info(ctx.Tr("repo.settings.webhook.delivery.success"))
ctx.Status(http.StatusOK)
2015-12-05 21:24:13 +03:00
}
}
2022-01-06 00:00:20 +03:00
// 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(ctx, w, hookTaskUUID); err != nil {
2022-01-06 00:00:20 +03:00
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))
}
2016-11-24 10:04:31 +03:00
// DeleteWebhook delete a webhook
2016-03-11 19:56:52 +03:00
func DeleteWebhook(ctx *context.Context) {
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
2015-12-05 21:24:13 +03:00
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/hooks")
2015-12-05 21:24:13 +03:00
}