Files

752 lines
23 KiB
Go
Raw Permalink Normal View History

2015-12-05 13:24:13 -05:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2017-05-29 09:17:15 +02:00
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2015-12-05 13:24:13 -05:00
2023-07-02 08:59:32 +08:00
package setting
2015-12-05 13:24:13 -05:00
import (
"errors"
"fmt"
"net/http"
2021-11-16 18:18:25 +00:00
"net/url"
2019-03-18 22:33:20 -04:00
"path"
2015-12-05 13:24:13 -05:00
"strings"
"code.gitea.io/gitea/models/db"
2021-11-28 19:58:28 +08:00
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user"
2021-11-10 13:13:16 +08:00
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
2019-05-11 18:21:34 +08:00
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
2020-12-25 09:59:32 +00:00
"code.gitea.io/gitea/modules/util"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2023-01-01 16:23:15 +01:00
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/context"
2022-12-29 03:57:15 +01:00
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
2021-11-10 13:13:16 +08:00
webhook_service "code.gitea.io/gitea/services/webhook"
2015-12-05 13:24:13 -05:00
)
const (
tplHooks templates.TplName = "repo/settings/webhook/base"
tplHookNew templates.TplName = "repo/settings/webhook/new"
tplOrgHookNew templates.TplName = "org/settings/hook_new"
tplUserHookNew templates.TplName = "user/settings/hook_new"
tplAdminHookNew templates.TplName = "admin/hook_new"
2015-12-05 13:24:13 -05:00
)
2016-11-24 15:04:31 +08:00
// Webhooks render web hooks list page
2016-03-11 11:56:52 -05:00
func Webhooks(ctx *context.Context) {
2015-12-05 13:24:13 -05:00
ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
ctx.Data["PageIsSettingsHooks"] = true
2019-03-18 22:33:20 -04:00
ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks")
2015-12-05 13:24:13 -05:00
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
2015-12-05 13:24:13 -05:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("GetWebhooksByRepoID", err)
2015-12-05 13:24:13 -05:00
return
}
ctx.Data["Webhooks"] = ws
ctx.HTML(http.StatusOK, tplHooks)
2015-12-05 13:24:13 -05:00
}
2023-03-10 15:28:32 +01:00
type ownerRepoCtx struct {
OwnerID int64
2020-03-08 22:08:05 +00:00
RepoID int64
IsAdmin bool
IsSystemWebhook bool
Link string
LinkNew string
NewTemplate templates.TplName
2015-12-05 13:24:13 -05:00
}
2023-03-10 15:28:32 +01:00
// getOwnerRepoCtx determines whether this is a repo, owner, or admin (both default and system) context.
func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
if ctx.Data["PageIsRepoSettings"] == true {
2023-03-10 15:28:32 +01:00
return &ownerRepoCtx{
2015-12-05 13:24:13 -05:00
RepoID: ctx.Repo.Repository.ID,
2019-03-18 22:33:20 -04:00
Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
2016-11-07 21:58:22 +01:00
NewTemplate: tplHookNew,
2015-12-05 13:24:13 -05:00
}, nil
}
if ctx.Data["PageIsOrgSettings"] == true {
2023-03-10 15:28:32 +01:00
return &ownerRepoCtx{
OwnerID: ctx.ContextUser.ID,
2019-03-18 22:33:20 -04:00
Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
2016-11-07 21:58:22 +01:00
NewTemplate: tplOrgHookNew,
2015-12-05 13:24:13 -05:00
}, nil
}
if ctx.Data["PageIsUserSettings"] == true {
2023-03-10 15:28:32 +01:00
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
}
2020-03-08 22:08:05 +00:00
if ctx.Data["PageIsAdmin"] == true {
2023-03-10 15:28:32 +01:00
return &ownerRepoCtx{
2020-03-08 22:08:05 +00:00
IsAdmin: true,
2024-12-24 21:47:45 +08:00
IsSystemWebhook: ctx.PathParam("configType") == "system-hooks",
Link: path.Join(setting.AppSubURL, "/-/admin/hooks"),
2024-12-24 21:47:45 +08:00
LinkNew: path.Join(setting.AppSubURL, "/-/admin/", ctx.PathParam("configType")),
2020-03-08 22:08:05 +00:00
NewTemplate: tplAdminHookNew,
2019-03-18 22:33:20 -04:00
}, nil
}
2023-03-10 15:28:32 +01:00
return nil, errors.New("unable to set OwnerRepo context")
2015-12-05 13:24:13 -05:00
}
2016-03-11 11:56:52 -05:00
func checkHookType(ctx *context.Context) string {
2024-12-24 21:47:45 +08:00
hookType := strings.ToLower(ctx.PathParam("type"))
2023-01-11 13:31:16 +08:00
if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2015-12-05 13:24:13 -05:00
return ""
}
return hookType
}
2016-11-24 15:04:31 +08:00
// WebhooksNew render creating webhook page
2016-03-11 11:56:52 -05:00
func WebhooksNew(ctx *context.Context) {
2015-12-05 13:24:13 -05:00
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
2023-01-01 16:23:15 +01:00
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
2015-12-05 13:24:13 -05:00
2023-03-10 15:28:32 +01:00
orCtx, err := getOwnerRepoCtx(ctx)
2015-12-05 13:24:13 -05:00
if err != nil {
2023-03-10 15:28:32 +01:00
ctx.ServerError("getOwnerRepoCtx", err)
2015-12-05 13:24:13 -05:00
return
}
2020-03-08 22:08:05 +00:00
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
2019-03-18 22:33:20 -04:00
} else {
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
}
2017-08-28 13:06:45 +08:00
hookType := checkHookType(ctx)
ctx.Data["HookType"] = hookType
2015-12-05 13:24:13 -05:00
if ctx.Written() {
return
}
2017-08-28 13:06:45 +08:00
if hookType == "discord" {
2023-07-04 20:36:08 +02:00
ctx.Data["DiscordHook"] = map[string]any{
2017-08-28 13:06:45 +08:00
"Username": "Gitea",
}
}
ctx.Data["BaseLink"] = orCtx.LinkNew
2024-02-15 14:59:48 +01:00
ctx.Data["BaseLinkNew"] = orCtx.LinkNew
2015-12-05 13:24:13 -05:00
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
2015-12-05 13:24:13 -05:00
}
2021-11-10 13:13:16 +08:00
// ParseHookEvent convert web form content to webhook.HookEvent
2023-01-01 16:23:15 +01:00
func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
return &webhook_module.HookEvent{
2015-12-05 13:24:13 -05:00
PushOnly: form.PushOnly(),
SendEverything: form.SendEverything(),
ChooseEvents: form.ChooseEvents(),
2023-01-01 16:23:15 +01:00
HookEvents: webhook_module.HookEvents{
2025-01-23 10:53:06 -08:00
webhook_module.HookEventCreate: form.Create,
webhook_module.HookEventDelete: form.Delete,
webhook_module.HookEventFork: form.Fork,
webhook_module.HookEventIssues: form.Issues,
webhook_module.HookEventIssueAssign: form.IssueAssign,
webhook_module.HookEventIssueLabel: form.IssueLabel,
webhook_module.HookEventIssueMilestone: form.IssueMilestone,
webhook_module.HookEventIssueComment: form.IssueComment,
webhook_module.HookEventRelease: form.Release,
webhook_module.HookEventPush: form.Push,
webhook_module.HookEventPullRequest: form.PullRequest,
webhook_module.HookEventPullRequestAssign: form.PullRequestAssign,
webhook_module.HookEventPullRequestLabel: form.PullRequestLabel,
webhook_module.HookEventPullRequestMilestone: form.PullRequestMilestone,
webhook_module.HookEventPullRequestComment: form.PullRequestComment,
webhook_module.HookEventPullRequestReview: form.PullRequestReview,
webhook_module.HookEventPullRequestSync: form.PullRequestSync,
webhook_module.HookEventPullRequestReviewRequest: form.PullRequestReviewRequest,
webhook_module.HookEventWiki: form.Wiki,
webhook_module.HookEventRepository: form.Repository,
webhook_module.HookEventPackage: form.Package,
2025-02-03 19:25:59 -08:00
webhook_module.HookEventStatus: form.Status,
2025-06-20 14:14:00 +02:00
webhook_module.HookEventWorkflowRun: form.WorkflowRun,
2025-03-11 18:40:38 +01:00
webhook_module.HookEventWorkflowJob: form.WorkflowJob,
2015-12-05 13:24:13 -05:00
},
2019-09-09 08:48:21 +03:00
BranchFilter: form.BranchFilter,
2015-12-05 13:24:13 -05:00
}
}
2022-08-23 08:52:35 +02:00
type webhookParams struct {
// Type should be imported from webhook package (webhook.XXX)
Type string
2022-08-11 17:48:23 +02:00
URL string
ContentType webhook.HookContentType
HTTPMethod string
WebhookForm forms.WebhookForm
2023-07-04 20:36:08 +02:00
Meta any
2022-08-11 17:48:23 +02:00
}
2022-08-23 08:52:35 +02:00
func createWebhook(ctx *context.Context, params webhookParams) {
2017-05-29 09:17:15 +02:00
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
2023-01-01 16:23:15 +01:00
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
2022-08-11 17:48:23 +02:00
ctx.Data["HookType"] = params.Type
2017-05-29 09:17:15 +02:00
2023-03-10 15:28:32 +01:00
orCtx, err := getOwnerRepoCtx(ctx)
2017-05-29 09:17:15 +02:00
if err != nil {
2023-03-10 15:28:32 +01:00
ctx.ServerError("getOwnerRepoCtx", err)
2017-05-29 09:17:15 +02:00
return
}
ctx.Data["BaseLink"] = orCtx.LinkNew
2017-05-29 09:17:15 +02:00
if ctx.HasError() {
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
2017-05-29 09:17:15 +02:00
return
}
2022-08-11 17:48:23 +02:00
var meta []byte
if params.Meta != nil {
meta, err = json.Marshal(params.Meta)
if err != nil {
ctx.ServerError("Marshal", err)
return
}
2017-05-29 09:17:15 +02:00
}
2021-11-10 13:13:16 +08:00
w := &webhook.Webhook{
2020-03-08 22:08:05 +00:00
RepoID: orCtx.RepoID,
2022-08-11 17:48:23 +02:00
URL: params.URL,
Name: strings.TrimSpace(params.WebhookForm.Name),
2022-08-11 17:48:23 +02:00
HTTPMethod: params.HTTPMethod,
ContentType: params.ContentType,
Secret: params.WebhookForm.Secret,
2022-08-11 17:48:23 +02:00
HookEvent: ParseHookEvent(params.WebhookForm),
IsActive: params.WebhookForm.Active,
Type: params.Type,
Meta: string(meta),
2023-03-10 15:28:32 +01:00
OwnerID: orCtx.OwnerID,
2020-03-08 22:08:05 +00:00
IsSystemWebhook: orCtx.IsSystemWebhook,
2017-05-29 09:17:15 +02:00
}
2022-11-03 19:23:20 +01:00
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
if err != nil {
ctx.ServerError("SetHeaderAuthorization", err)
return
}
2017-05-29 09:17:15 +02:00
if err := w.UpdateEvent(); err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("UpdateEvent", err)
2017-05-29 09:17:15 +02:00
return
} else if err := webhook.CreateWebhook(ctx, w); err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("CreateWebhook", err)
2017-05-29 09:17:15 +02:00
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
2019-03-18 22:33:20 -04:00
ctx.Redirect(orCtx.Link)
2017-05-29 09:17:15 +02:00
}
2022-08-23 08:52:35 +02: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.StatusOK, orCtx.NewTemplate)
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.Name = strings.TrimSpace(params.WebhookForm.Name)
2022-08-23 08:52:35 +02:00
w.ContentType = params.ContentType
w.Secret = params.WebhookForm.Secret
2022-08-23 08:52:35 +02:00
w.HookEvent = ParseHookEvent(params.WebhookForm)
w.IsActive = params.WebhookForm.Active
w.HTTPMethod = params.HTTPMethod
w.Meta = string(meta)
2022-11-03 19:23:20 +01:00
err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
if err != nil {
ctx.ServerError("SetHeaderAuthorization", err)
return
}
2022-08-23 08:52:35 +02:00
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := webhook.UpdateWebhook(ctx, w); err != nil {
2022-08-23 08:52:35 +02: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))
}
2022-08-11 17:48:23 +02:00
// GiteaHooksNewPost response for creating Gitea webhook
func GiteaHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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 {
2022-08-11 17:48:23 +02:00
form := web.GetForm(ctx).(*forms.NewWebhookForm)
2015-12-05 13:24:13 -05:00
2022-08-11 17:48:23 +02:00
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
2015-12-05 13:24:13 -05:00
}
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.GITEA,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: contentType,
HTTPMethod: form.HTTPMethod,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
2022-08-11 17:48:23 +02:00
}
2022-08-23 08:52:35 +02:00
// GogsHooksNewPost response for creating Gogs webhook
2022-08-11 17:48:23 +02:00
func GogsHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02: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 {
2022-08-11 17:48:23 +02:00
form := web.GetForm(ctx).(*forms.NewGogshookForm)
2015-12-05 13:24:13 -05:00
2021-11-10 13:13:16 +08:00
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
2015-12-05 13:24:13 -05:00
}
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.GOGS,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: contentType,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
2015-12-05 13:24:13 -05:00
}
2022-08-23 08:52:35 +02:00
// DiscordHooksNewPost response for creating Discord webhook
2021-01-26 23:36:53 +08:00
func DiscordHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2017-08-28 13:06:45 +08:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.DISCORD,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.DiscordMeta{
Username: form.Username,
IconURL: form.IconURL,
},
2022-08-23 08:52:35 +02:00
}
2017-08-28 13:06:45 +08:00
}
2022-08-23 08:52:35 +02:00
// DingtalkHooksNewPost response for creating Dingtalk webhook
2021-01-26 23:36:53 +08:00
func DingtalkHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2017-11-21 12:26:43 +08:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.DINGTALK,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
2017-11-21 12:26:43 +08:00
}
2022-08-23 08:52:35 +02:00
// TelegramHooksNewPost response for creating Telegram webhook
2021-01-26 23:36:53 +08:00
func TelegramHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2019-04-18 22:45:02 -04:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
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)),
2022-08-11 17:48:23 +02:00
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.TelegramMeta{
BotToken: form.BotToken,
ChatID: form.ChatID,
ThreadID: form.ThreadID,
2022-08-11 17:48:23 +02:00
},
2022-08-23 08:52:35 +02:00
}
2019-04-18 22:45:02 -04:00
}
2022-08-23 08:52:35 +02:00
// MatrixHooksNewPost response for creating Matrix webhook
2021-01-26 23:36:53 +08:00
func MatrixHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
createWebhook(ctx, matrixHookParams(ctx))
}
// MatrixHooksEditPost response for editing Matrix webhook
func MatrixHooksEditPost(ctx *context.Context) {
editWebhook(ctx, matrixHookParams(ctx))
}
func matrixRoomIDEncode(roomID string) string {
// See https://spec.matrix.org/latest/appendices/#room-ids
// Some (unrelated) demo links: https://spec.matrix.org/latest/appendices/#matrixto-navigation
// API spec: https://spec.matrix.org/v1.18/client-server-api/#sending-events-to-a-room
// Some of their examples show links like: "PUT /rooms/!roomid:domain/state/m.example.event"
return strings.NewReplacer("%21", "!", "%3A", ":").Replace(url.PathEscape(roomID))
}
2022-08-23 08:52:35 +02:00
func matrixHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
2020-03-28 14:09:55 +01:00
// TODO: need to migrate to the latest (v3) API: https://spec.matrix.org/v1.18/client-server-api/
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.MATRIX,
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, matrixRoomIDEncode(form.RoomID)),
2022-08-11 17:48:23 +02:00
ContentType: webhook.ContentTypeJSON,
HTTPMethod: http.MethodPut,
WebhookForm: form.WebhookForm,
Meta: &webhook_service.MatrixMeta{
HomeserverURL: form.HomeserverURL,
Room: form.RoomID,
MessageType: form.MessageType,
},
2022-08-23 08:52:35 +02:00
}
2020-03-28 14:09:55 +01:00
}
2022-08-23 08:52:35 +02:00
// MSTeamsHooksNewPost response for creating MSTeams webhook
2021-01-26 23:36:53 +08:00
func MSTeamsHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2019-04-20 00:18:06 +10:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.MSTEAMS,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
2019-04-20 00:18:06 +10:00
}
2022-08-23 08:52:35 +02:00
// SlackHooksNewPost response for creating Slack webhook
2021-01-26 23:36:53 +08:00
func SlackHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2015-12-05 13:24:13 -05:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.SLACK,
2022-08-11 17:48:23 +02:00
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,
},
2022-08-23 08:52:35 +02:00
}
2015-12-05 13:24:13 -05:00
}
2022-08-23 08:52:35 +02:00
// FeishuHooksNewPost response for creating Feishu webhook
2021-01-26 23:36:53 +08:00
func FeishuHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2020-02-12 16:48:28 +08:00
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.FEISHU,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
2020-02-12 16:48:28 +08:00
}
2022-08-23 08:52:35 +02:00
// WechatworkHooksNewPost response for creating Wechatwork webhook
func WechatworkHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02:00
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)
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.WECHATWORK,
2022-08-11 17:48:23 +02:00
URL: form.PayloadURL,
ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm,
2022-08-23 08:52:35 +02:00
}
}
2022-08-23 08:52:35 +02:00
// PackagistHooksNewPost response for creating Packagist webhook
2022-01-23 14:46:30 +01:00
func PackagistHooksNewPost(ctx *context.Context) {
2022-08-23 08:52:35 +02: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 {
2022-01-23 14:46:30 +01:00
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
2022-08-23 08:52:35 +02:00
return webhookParams{
2023-01-01 16:23:15 +01:00
Type: webhook_module.PACKAGIST,
2022-08-11 17:48:23 +02:00
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 08:52:35 +02:00
}
2022-01-23 14:46:30 +01:00
}
2023-03-10 15:28:32 +01:00
func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
orCtx, err := getOwnerRepoCtx(ctx)
2015-12-05 13:24:13 -05:00
if err != nil {
2023-03-10 15:28:32 +01:00
ctx.ServerError("getOwnerRepoCtx", err)
2015-12-05 13:24:13 -05:00
return nil, nil
}
ctx.Data["BaseLink"] = orCtx.Link
ctx.Data["BaseLinkNew"] = orCtx.LinkNew
2015-12-05 13:24:13 -05:00
2021-11-10 13:13:16 +08:00
var w *webhook.Webhook
if orCtx.RepoID > 0 {
2024-12-24 21:47:45 +08:00
w, err = webhook.GetWebhookByRepoID(ctx, orCtx.RepoID, ctx.PathParamInt64("id"))
2023-03-10 15:28:32 +01:00
} else if orCtx.OwnerID > 0 {
2024-12-24 21:47:45 +08:00
w, err = webhook.GetWebhookByOwnerID(ctx, orCtx.OwnerID, ctx.PathParamInt64("id"))
} else if orCtx.IsAdmin {
2024-12-24 21:47:45 +08:00
w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.PathParamInt64("id"))
}
if err != nil || w == nil {
2021-11-10 13:13:16 +08:00
if webhook.IsErrWebhookNotExist(err) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2015-12-05 13:24:13 -05:00
} else {
2018-01-10 22:34:17 +01:00
ctx.ServerError("GetWebhookByID", err)
2015-12-05 13:24:13 -05:00
}
return nil, nil
}
2020-12-10 01:20:13 +08:00
ctx.Data["HookType"] = w.Type
switch w.Type {
2023-01-01 16:23:15 +01:00
case webhook_module.SLACK:
2021-11-10 13:13:16 +08:00
ctx.Data["SlackHook"] = webhook_service.GetSlackHook(w)
2023-01-01 16:23:15 +01:00
case webhook_module.DISCORD:
2021-11-10 13:13:16 +08:00
ctx.Data["DiscordHook"] = webhook_service.GetDiscordHook(w)
2023-01-01 16:23:15 +01:00
case webhook_module.TELEGRAM:
2021-11-10 13:13:16 +08:00
ctx.Data["TelegramHook"] = webhook_service.GetTelegramHook(w)
2023-01-01 16:23:15 +01:00
case webhook_module.MATRIX:
2021-11-10 13:13:16 +08:00
ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
2023-01-01 16:23:15 +01:00
case webhook_module.PACKAGIST:
2022-01-23 14:46:30 +01:00
ctx.Data["PackagistHook"] = webhook_service.GetPackagistHook(w)
2015-12-05 13:24:13 -05:00
}
ctx.Data["History"], err = w.History(ctx, 1)
2015-12-05 13:24:13 -05:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("History", err)
2015-12-05 13:24:13 -05:00
}
return orCtx, w
}
2016-11-24 15:04:31 +08:00
// WebHooksEdit render editing web hook page
2016-03-11 11:56:52 -05:00
func WebHooksEdit(ctx *context.Context) {
2015-12-05 13:24:13 -05: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 13:24:13 -05:00
}
2016-11-24 15:04:31 +08:00
// TestWebhook test if web hook is work fine
2016-03-11 11:56:52 -05:00
func TestWebhook(ctx *context.Context) {
2024-12-24 21:47:45 +08:00
hookID := ctx.PathParamInt64("id")
w, err := webhook.GetWebhookByRepoID(ctx, ctx.Repo.Repository.ID, hookID)
2017-08-29 22:55:24 +08:00
if err != nil {
2022-10-21 18:21:56 +02:00
ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
ctx.Status(http.StatusInternalServerError)
2017-08-29 22:55:24 +08:00
return
}
// Grab latest commit or fake one if it's empty repository.
2025-01-20 15:43:49 +08:00
// Note: in old code, the "ctx.Repo.Commit" is the last commit of the default branch.
// New code doesn't set that commit, so it always uses the fake commit to test webhook.
commit := ctx.Repo.Commit
if commit == nil {
ghost := user_model.NewGhostUser()
objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName)
commit = &git.Commit{
2023-12-17 19:56:08 +08:00
ID: objectFormat.EmptyObjectID(),
Author: ghost.NewGitSig(),
Committer: ghost.NewGitSig(),
CommitMessage: "This is a fake commit",
}
}
apiUser := convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone)
apiCommit := &api.PayloadCommit{
ID: commit.ID.String(),
Message: commit.Message(),
2021-11-16 18:18:25 +00:00
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 13:24:13 -05: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 13:24:13 -05:00
}
2023-01-01 16:23:15 +01:00
if err := webhook_service.PrepareWebhook(ctx, w, webhook_module.HookEventPush, p); err != nil {
2017-08-29 22:55:24 +08:00
ctx.Flash.Error("PrepareWebhook: " + err.Error())
ctx.Status(http.StatusInternalServerError)
2015-12-05 13:24:13 -05:00
} else {
2022-01-05 22:00:20 +01:00
ctx.Flash.Info(ctx.Tr("repo.settings.webhook.delivery.success"))
ctx.Status(http.StatusOK)
2015-12-05 13:24:13 -05:00
}
}
2022-01-05 22:00:20 +01:00
// ReplayWebhook replays a webhook
func ReplayWebhook(ctx *context.Context) {
2024-12-24 21:47:45 +08:00
hookTaskUUID := ctx.PathParam("uuid")
2022-01-05 22:00:20 +01:00
orCtx, w := checkWebhook(ctx)
if ctx.Written() {
return
}
2022-10-21 18:21:56 +02:00
if err := webhook_service.ReplayHookTask(ctx, w, hookTaskUUID); err != nil {
2022-01-05 22:00:20 +01:00
if webhook.IsErrHookTaskNotExist(err) {
2025-02-17 14:13:17 +08:00
ctx.NotFound(nil)
2022-01-05 22:00:20 +01:00
} 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 15:04:31 +08:00
// DeleteWebhook delete a webhook
2016-03-11 11:56:52 -05: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 13:24:13 -05:00
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
}
ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/hooks")
2015-12-05 13:24:13 -05:00
}