Merge pull request '[REFACTOR] webhook settings: merge Create/Update endpoints' (#2843) from oliverpool/forgejo:webhook_3_endpoint_merge into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2843 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
883cb2d542
17 changed files with 342 additions and 486 deletions
|
@ -79,8 +79,8 @@ func GetInclude(field reflect.StructField) string {
|
||||||
return getRuleBody(field, "Include(")
|
return getRuleBody(field, "Include(")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validate TODO:
|
// Validate populates the data with validation error (if any).
|
||||||
func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Locale) binding.Errors {
|
func Validate(errs binding.Errors, data map[string]any, f any, l translation.Locale) binding.Errors {
|
||||||
if errs.Len() == 0 {
|
if errs.Len() == 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,14 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web/middleware"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
webhook_service "code.gitea.io/gitea/services/webhook"
|
webhook_service "code.gitea.io/gitea/services/webhook"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -201,6 +203,29 @@ type webhookParams struct {
|
||||||
Meta any
|
Meta any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func createWebhook(ctx *context.Context, params webhookParams) {
|
func createWebhook(ctx *context.Context, params webhookParams) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
||||||
ctx.Data["PageIsSettingsHooks"] = true
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
@ -260,6 +285,29 @@ func createWebhook(ctx *context.Context, params webhookParams) {
|
||||||
ctx.Redirect(orCtx.Link)
|
ctx.Redirect(orCtx.Link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func editWebhook(ctx *context.Context, params webhookParams) {
|
func editWebhook(ctx *context.Context, params webhookParams) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
|
||||||
ctx.Data["PageIsSettingsHooks"] = true
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
@ -312,304 +360,6 @@ func editWebhook(ctx *context.Context, params webhookParams) {
|
||||||
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
|
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForgejoHooksNewPost response for creating Forgejo webhook
|
|
||||||
func ForgejoHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, forgejoHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForgejoHooksEditPost response for editing Forgejo webhook
|
|
||||||
func ForgejoHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, forgejoHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func forgejoHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewWebhookForm)
|
|
||||||
|
|
||||||
contentType := webhook.ContentTypeJSON
|
|
||||||
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
|
|
||||||
contentType = webhook.ContentTypeForm
|
|
||||||
}
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.FORGEJO,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: contentType,
|
|
||||||
Secret: form.Secret,
|
|
||||||
HTTPMethod: form.HTTPMethod,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GiteaHooksNewPost response for creating Gitea webhook
|
|
||||||
func GiteaHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, giteaHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GiteaHooksEditPost response for editing Gitea webhook
|
|
||||||
func GiteaHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, giteaHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func giteaHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewWebhookForm)
|
|
||||||
|
|
||||||
contentType := webhook.ContentTypeJSON
|
|
||||||
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
|
|
||||||
contentType = webhook.ContentTypeForm
|
|
||||||
}
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.GITEA,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: contentType,
|
|
||||||
Secret: form.Secret,
|
|
||||||
HTTPMethod: form.HTTPMethod,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GogsHooksNewPost response for creating Gogs webhook
|
|
||||||
func GogsHooksNewPost(ctx *context.Context) {
|
|
||||||
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)
|
|
||||||
|
|
||||||
contentType := webhook.ContentTypeJSON
|
|
||||||
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
|
|
||||||
contentType = webhook.ContentTypeForm
|
|
||||||
}
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.GOGS,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: contentType,
|
|
||||||
Secret: form.Secret,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscordHooksNewPost response for creating Discord webhook
|
|
||||||
func DiscordHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, discordHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscordHooksEditPost response for editing Discord webhook
|
|
||||||
func DiscordHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, discordHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func discordHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewDiscordHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.DISCORD,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
Meta: &webhook_service.DiscordMeta{
|
|
||||||
Username: form.Username,
|
|
||||||
IconURL: form.IconURL,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DingtalkHooksNewPost response for creating Dingtalk webhook
|
|
||||||
func DingtalkHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, dingtalkHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DingtalkHooksEditPost response for editing Dingtalk webhook
|
|
||||||
func DingtalkHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, dingtalkHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func dingtalkHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewDingtalkHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.DINGTALK,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TelegramHooksNewPost response for creating Telegram webhook
|
|
||||||
func TelegramHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, telegramHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TelegramHooksEditPost response for editing Telegram webhook
|
|
||||||
func TelegramHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, telegramHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func telegramHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewTelegramHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.TELEGRAM,
|
|
||||||
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&message_thread_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID), url.QueryEscape(form.ThreadID)),
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
Meta: &webhook_service.TelegramMeta{
|
|
||||||
BotToken: form.BotToken,
|
|
||||||
ChatID: form.ChatID,
|
|
||||||
ThreadID: form.ThreadID,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatrixHooksNewPost response for creating Matrix webhook
|
|
||||||
func MatrixHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, matrixHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatrixHooksEditPost response for editing Matrix webhook
|
|
||||||
func MatrixHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, matrixHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func matrixHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.MATRIX,
|
|
||||||
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
HTTPMethod: http.MethodPut,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
Meta: &webhook_service.MatrixMeta{
|
|
||||||
HomeserverURL: form.HomeserverURL,
|
|
||||||
Room: form.RoomID,
|
|
||||||
MessageType: form.MessageType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MSTeamsHooksNewPost response for creating MSTeams webhook
|
|
||||||
func MSTeamsHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, mSTeamsHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MSTeamsHooksEditPost response for editing MSTeams webhook
|
|
||||||
func MSTeamsHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, mSTeamsHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mSTeamsHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewMSTeamsHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.MSTEAMS,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SlackHooksNewPost response for creating Slack webhook
|
|
||||||
func SlackHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, slackHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SlackHooksEditPost response for editing Slack webhook
|
|
||||||
func SlackHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, slackHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func slackHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewSlackHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.SLACK,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
Meta: &webhook_service.SlackMeta{
|
|
||||||
Channel: strings.TrimSpace(form.Channel),
|
|
||||||
Username: form.Username,
|
|
||||||
IconURL: form.IconURL,
|
|
||||||
Color: form.Color,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FeishuHooksNewPost response for creating Feishu webhook
|
|
||||||
func FeishuHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, feishuHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FeishuHooksEditPost response for editing Feishu webhook
|
|
||||||
func FeishuHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, feishuHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func feishuHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.FEISHU,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WechatworkHooksNewPost response for creating Wechatwork webhook
|
|
||||||
func WechatworkHooksNewPost(ctx *context.Context) {
|
|
||||||
createWebhook(ctx, wechatworkHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WechatworkHooksEditPost response for editing Wechatwork webhook
|
|
||||||
func WechatworkHooksEditPost(ctx *context.Context) {
|
|
||||||
editWebhook(ctx, wechatworkHookParams(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func wechatworkHookParams(ctx *context.Context) webhookParams {
|
|
||||||
form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
|
|
||||||
|
|
||||||
return webhookParams{
|
|
||||||
Type: webhook_module.WECHATWORK,
|
|
||||||
URL: form.PayloadURL,
|
|
||||||
ContentType: webhook.ContentTypeJSON,
|
|
||||||
WebhookForm: form.WebhookForm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PackagistHooksNewPost response for creating Packagist webhook
|
|
||||||
func PackagistHooksNewPost(ctx *context.Context) {
|
|
||||||
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)
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
|
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
|
||||||
orCtx, err := getOwnerRepoCtx(ctx)
|
orCtx, err := getOwnerRepoCtx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -402,33 +402,11 @@ func registerRoutes(m *web.Route) {
|
||||||
|
|
||||||
addWebhookAddRoutes := func() {
|
addWebhookAddRoutes := func() {
|
||||||
m.Get("/{type}/new", repo_setting.WebhooksNew)
|
m.Get("/{type}/new", repo_setting.WebhooksNew)
|
||||||
m.Post("/forgejo/new", web.Bind(forms.NewWebhookForm{}), repo_setting.ForgejoHooksNewPost)
|
m.Post("/{type}/new", repo_setting.WebhookCreate)
|
||||||
m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksNewPost)
|
|
||||||
m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksNewPost)
|
|
||||||
m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksNewPost)
|
|
||||||
m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksNewPost)
|
|
||||||
m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksNewPost)
|
|
||||||
m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksNewPost)
|
|
||||||
m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksNewPost)
|
|
||||||
m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksNewPost)
|
|
||||||
m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksNewPost)
|
|
||||||
m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksNewPost)
|
|
||||||
m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksNewPost)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addWebhookEditRoutes := func() {
|
addWebhookEditRoutes := func() {
|
||||||
m.Post("/forgejo/{id}", web.Bind(forms.NewWebhookForm{}), repo_setting.ForgejoHooksEditPost)
|
m.Post("/{type}/{id:[0-9]+}", repo_setting.WebhookUpdate)
|
||||||
m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksEditPost)
|
|
||||||
m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksEditPost)
|
|
||||||
m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksEditPost)
|
|
||||||
m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksEditPost)
|
|
||||||
m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksEditPost)
|
|
||||||
m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksEditPost)
|
|
||||||
m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksEditPost)
|
|
||||||
m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksEditPost)
|
|
||||||
m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksEditPost)
|
|
||||||
m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksEditPost)
|
|
||||||
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksEditPost)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addSettingsVariablesRoutes := func() {
|
addSettingsVariablesRoutes := func() {
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
"code.gitea.io/gitea/modules/web/middleware"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/webhook"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
@ -279,162 +278,6 @@ func (f WebhookForm) ChooseEvents() bool {
|
||||||
return f.Events == "choose_events"
|
return f.Events == "choose_events"
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWebhookForm form for creating web hook
|
|
||||||
type NewWebhookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
HTTPMethod string `binding:"Required;In(POST,GET)"`
|
|
||||||
ContentType int `binding:"Required"`
|
|
||||||
Secret string
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGogshookForm form for creating gogs hook
|
|
||||||
type NewGogshookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
ContentType int `binding:"Required"`
|
|
||||||
Secret string
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSlackHookForm form for creating slack hook
|
|
||||||
type NewSlackHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
Channel string `binding:"Required"`
|
|
||||||
Username string
|
|
||||||
IconURL string
|
|
||||||
Color string
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
|
|
||||||
errs = append(errs, binding.Error{
|
|
||||||
FieldNames: []string{"Channel"},
|
|
||||||
Classification: "",
|
|
||||||
Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_channel_name"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDiscordHookForm form for creating discord hook
|
|
||||||
type NewDiscordHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
Username string
|
|
||||||
IconURL string
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDingtalkHookForm form for creating dingtalk hook
|
|
||||||
type NewDingtalkHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTelegramHookForm form for creating telegram hook
|
|
||||||
type NewTelegramHookForm struct {
|
|
||||||
BotToken string `binding:"Required"`
|
|
||||||
ChatID string `binding:"Required"`
|
|
||||||
ThreadID string
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMatrixHookForm form for creating Matrix hook
|
|
||||||
type NewMatrixHookForm struct {
|
|
||||||
HomeserverURL string `binding:"Required;ValidUrl"`
|
|
||||||
RoomID string `binding:"Required"`
|
|
||||||
MessageType int
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMSTeamsHookForm form for creating MS Teams hook
|
|
||||||
type NewMSTeamsHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFeishuHookForm form for creating feishu hook
|
|
||||||
type NewFeishuHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWechatWorkHookForm form for creating wechatwork hook
|
|
||||||
type NewWechatWorkHookForm struct {
|
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPackagistHookForm form for creating packagist hook
|
|
||||||
type NewPackagistHookForm struct {
|
|
||||||
Username string `binding:"Required"`
|
|
||||||
APIToken string `binding:"Required"`
|
|
||||||
PackageURL string `binding:"Required;ValidUrl"`
|
|
||||||
WebhookForm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the fields
|
|
||||||
func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
||||||
ctx := context.GetValidateContext(req)
|
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// .___
|
// .___
|
||||||
// | | ______ ________ __ ____
|
// | | ______ ________ __ ____
|
||||||
// | |/ ___// ___/ | \_/ __ \
|
// | |/ ___// ___/ | \_/ __ \
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Handler = defaultHandler{}
|
var _ Handler = defaultHandler{}
|
||||||
|
@ -35,6 +36,30 @@ func (dh defaultHandler) Type() webhook_module.HookType {
|
||||||
|
|
||||||
func (defaultHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
func (defaultHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
||||||
|
|
||||||
|
func (defaultHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
HTTPMethod string `binding:"Required;In(POST,GET)"`
|
||||||
|
ContentType int `binding:"Required"`
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
contentType := webhook_model.ContentTypeJSON
|
||||||
|
if webhook_model.HookContentType(form.ContentType) == webhook_model.ContentTypeForm {
|
||||||
|
contentType = webhook_model.ContentTypeForm
|
||||||
|
}
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: contentType,
|
||||||
|
Secret: form.Secret,
|
||||||
|
HTTPMethod: form.HTTPMethod,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) {
|
func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) {
|
||||||
switch w.HTTPMethod {
|
switch w.HTTPMethod {
|
||||||
case "":
|
case "":
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
|
||||||
dingtalk "gitea.com/lunny/dingtalk_webhook"
|
dingtalk "gitea.com/lunny/dingtalk_webhook"
|
||||||
)
|
)
|
||||||
|
@ -23,6 +24,22 @@ type dingtalkHandler struct{}
|
||||||
|
|
||||||
func (dingtalkHandler) Type() webhook_module.HookType { return webhook_module.DINGTALK }
|
func (dingtalkHandler) Type() webhook_module.HookType { return webhook_module.DINGTALK }
|
||||||
func (dingtalkHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
func (dingtalkHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
||||||
|
func (dingtalkHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// DingtalkPayload represents
|
// DingtalkPayload represents
|
||||||
|
|
|
@ -20,12 +20,35 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type discordHandler struct{}
|
type discordHandler struct{}
|
||||||
|
|
||||||
func (discordHandler) Type() webhook_module.HookType { return webhook_module.DISCORD }
|
func (discordHandler) Type() webhook_module.HookType { return webhook_module.DISCORD }
|
||||||
|
|
||||||
|
func (discordHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
Username string
|
||||||
|
IconURL string
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: &DiscordMeta{
|
||||||
|
Username: form.Username,
|
||||||
|
IconURL: form.IconURL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// DiscordEmbedFooter for Embed Footer Structure.
|
// DiscordEmbedFooter for Embed Footer Structure.
|
||||||
DiscordEmbedFooter struct {
|
DiscordEmbedFooter struct {
|
||||||
|
|
|
@ -13,11 +13,30 @@ import (
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type feishuHandler struct{}
|
type feishuHandler struct{}
|
||||||
|
|
||||||
func (feishuHandler) Type() webhook_module.HookType { return webhook_module.FEISHU }
|
func (feishuHandler) Type() webhook_module.HookType { return webhook_module.FEISHU }
|
||||||
|
|
||||||
|
func (feishuHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (feishuHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
func (feishuHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
|
@ -4,9 +4,36 @@
|
||||||
package webhook
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gogsHandler struct{ defaultHandler }
|
type gogsHandler struct{ defaultHandler }
|
||||||
|
|
||||||
func (gogsHandler) Type() webhook_module.HookType { return webhook_module.GOGS }
|
func (gogsHandler) Type() webhook_module.HookType { return webhook_module.GOGS }
|
||||||
|
|
||||||
|
func (gogsHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
ContentType int `binding:"Required"`
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
contentType := webhook_model.ContentTypeJSON
|
||||||
|
if webhook_model.HookContentType(form.ContentType) == webhook_model.ContentTypeForm {
|
||||||
|
contentType = webhook_model.ContentTypeForm
|
||||||
|
}
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: contentType,
|
||||||
|
Secret: form.Secret,
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,12 +22,40 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type matrixHandler struct{}
|
type matrixHandler struct{}
|
||||||
|
|
||||||
func (matrixHandler) Type() webhook_module.HookType { return webhook_module.MATRIX }
|
func (matrixHandler) Type() webhook_module.HookType { return webhook_module.MATRIX }
|
||||||
|
|
||||||
|
func (matrixHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
HomeserverURL string `binding:"Required;ValidUrl"`
|
||||||
|
RoomID string `binding:"Required"`
|
||||||
|
MessageType int
|
||||||
|
|
||||||
|
// enforce requirement of authorization_header
|
||||||
|
// (value will still be set in the embedded WebhookForm)
|
||||||
|
AuthorizationHeader string `binding:"Required"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPut,
|
||||||
|
Metadata: &MatrixMeta{
|
||||||
|
HomeserverURL: form.HomeserverURL,
|
||||||
|
Room: form.RoomID,
|
||||||
|
MessageType: form.MessageType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (matrixHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
func (matrixHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
||||||
meta := &MatrixMeta{}
|
meta := &MatrixMeta{}
|
||||||
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
|
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type msteamsHandler struct{}
|
type msteamsHandler struct{}
|
||||||
|
@ -22,6 +23,23 @@ type msteamsHandler struct{}
|
||||||
func (msteamsHandler) Type() webhook_module.HookType { return webhook_module.MSTEAMS }
|
func (msteamsHandler) Type() webhook_module.HookType { return webhook_module.MSTEAMS }
|
||||||
func (msteamsHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
func (msteamsHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
||||||
|
|
||||||
|
func (msteamsHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// MSTeamsFact for Fact Structure
|
// MSTeamsFact for Fact Structure
|
||||||
MSTeamsFact struct {
|
MSTeamsFact struct {
|
||||||
|
|
|
@ -7,17 +7,42 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type packagistHandler struct{}
|
type packagistHandler struct{}
|
||||||
|
|
||||||
func (packagistHandler) Type() webhook_module.HookType { return webhook_module.PACKAGIST }
|
func (packagistHandler) Type() webhook_module.HookType { return webhook_module.PACKAGIST }
|
||||||
|
|
||||||
|
func (packagistHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
Username string `binding:"Required"`
|
||||||
|
APIToken string `binding:"Required"`
|
||||||
|
PackageURL string `binding:"Required;ValidUrl"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: &PackagistMeta{
|
||||||
|
Username: form.Username,
|
||||||
|
APIToken: form.APIToken,
|
||||||
|
PackageURL: form.PackageURL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// PackagistPayload represents a packagist payload
|
// PackagistPayload represents a packagist payload
|
||||||
// as expected by https://packagist.org/about
|
// as expected by https://packagist.org/about
|
||||||
|
|
|
@ -17,12 +17,59 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
gitea_context "code.gitea.io/gitea/services/context"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
type slackHandler struct{}
|
type slackHandler struct{}
|
||||||
|
|
||||||
func (slackHandler) Type() webhook_module.HookType { return webhook_module.SLACK }
|
func (slackHandler) Type() webhook_module.HookType { return webhook_module.SLACK }
|
||||||
|
|
||||||
|
type slackForm struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
Channel string `binding:"Required"`
|
||||||
|
Username string
|
||||||
|
IconURL string
|
||||||
|
Color string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ binding.Validator = &slackForm{}
|
||||||
|
|
||||||
|
// Validate implements binding.Validator.
|
||||||
|
func (s *slackForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
|
ctx := gitea_context.GetWebContext(req)
|
||||||
|
if !IsValidSlackChannel(strings.TrimSpace(s.Channel)) {
|
||||||
|
errs = append(errs, binding.Error{
|
||||||
|
FieldNames: []string{"Channel"},
|
||||||
|
Classification: "",
|
||||||
|
Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_channel_name"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slackHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form slackForm
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: &SlackMeta{
|
||||||
|
Channel: strings.TrimSpace(form.Channel),
|
||||||
|
Username: form.Username,
|
||||||
|
IconURL: form.IconURL,
|
||||||
|
Color: form.Color,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SlackMeta contains the slack metadata
|
// SlackMeta contains the slack metadata
|
||||||
type SlackMeta struct {
|
type SlackMeta struct {
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||||
|
@ -15,12 +16,36 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type telegramHandler struct{}
|
type telegramHandler struct{}
|
||||||
|
|
||||||
func (telegramHandler) Type() webhook_module.HookType { return webhook_module.TELEGRAM }
|
func (telegramHandler) Type() webhook_module.HookType { return webhook_module.TELEGRAM }
|
||||||
|
|
||||||
|
func (telegramHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
BotToken string `binding:"Required"`
|
||||||
|
ChatID string `binding:"Required"`
|
||||||
|
ThreadID string
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&message_thread_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID), url.QueryEscape(form.ThreadID)),
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: &TelegramMeta{
|
||||||
|
BotToken: form.BotToken,
|
||||||
|
ChatID: form.ChatID,
|
||||||
|
ThreadID: form.ThreadID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// TelegramPayload represents
|
// TelegramPayload represents
|
||||||
TelegramPayload struct {
|
TelegramPayload struct {
|
||||||
|
|
|
@ -23,14 +23,27 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
Type() webhook_module.HookType
|
Type() webhook_module.HookType
|
||||||
NewRequest(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error)
|
|
||||||
Metadata(*webhook_model.Webhook) any
|
Metadata(*webhook_model.Webhook) any
|
||||||
|
// FormFields provides a function to bind the request to the form.
|
||||||
|
// If form implements the [binding.Validator] interface, the Validate method will be called
|
||||||
|
FormFields(bind func(form any)) FormFields
|
||||||
|
NewRequest(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormFields struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
URL string
|
||||||
|
ContentType webhook_model.HookContentType
|
||||||
|
Secret string
|
||||||
|
HTTPMethod string
|
||||||
|
Metadata any
|
||||||
}
|
}
|
||||||
|
|
||||||
var webhookHandlers = []Handler{
|
var webhookHandlers = []Handler{
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wechatworkHandler struct{}
|
type wechatworkHandler struct{}
|
||||||
|
@ -20,6 +21,23 @@ type wechatworkHandler struct{}
|
||||||
func (wechatworkHandler) Type() webhook_module.HookType { return webhook_module.WECHATWORK }
|
func (wechatworkHandler) Type() webhook_module.HookType { return webhook_module.WECHATWORK }
|
||||||
func (wechatworkHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
func (wechatworkHandler) Metadata(*webhook_model.Webhook) any { return nil }
|
||||||
|
|
||||||
|
func (wechatworkHandler) FormFields(bind func(any)) FormFields {
|
||||||
|
var form struct {
|
||||||
|
forms.WebhookForm
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
}
|
||||||
|
bind(&form)
|
||||||
|
|
||||||
|
return FormFields{
|
||||||
|
WebhookForm: form.WebhookForm,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: webhook_model.ContentTypeJSON,
|
||||||
|
Secret: "",
|
||||||
|
HTTPMethod: http.MethodPost,
|
||||||
|
Metadata: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// WechatworkPayload represents
|
// WechatworkPayload represents
|
||||||
WechatworkPayload struct {
|
WechatworkPayload struct {
|
||||||
|
|
|
@ -203,8 +203,8 @@ func TestWebhookForms(t *testing.T) {
|
||||||
"homeserver_url": "https://matrix.example.com",
|
"homeserver_url": "https://matrix.example.com",
|
||||||
"room_id": "123",
|
"room_id": "123",
|
||||||
"authorization_header": "Bearer 123456",
|
"authorization_header": "Bearer 123456",
|
||||||
// }, map[string]string{ // authorization_header is actually required, but not enforced (yet)
|
}, map[string]string{
|
||||||
// "authorization_header": "",
|
"authorization_header": "",
|
||||||
}))
|
}))
|
||||||
t.Run("matrix/optional", testWebhookForms("matrix", session, map[string]string{
|
t.Run("matrix/optional", testWebhookForms("matrix", session, map[string]string{
|
||||||
"homeserver_url": "https://matrix.example.com",
|
"homeserver_url": "https://matrix.example.com",
|
||||||
|
|
Loading…
Reference in a new issue