Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

webhook.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "net/url"
  10. "path"
  11. "strings"
  12. "code.gitea.io/gitea/models/perm"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/models/webhook"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/context"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/json"
  19. "code.gitea.io/gitea/modules/setting"
  20. api "code.gitea.io/gitea/modules/structs"
  21. "code.gitea.io/gitea/modules/util"
  22. "code.gitea.io/gitea/modules/web"
  23. webhook_module "code.gitea.io/gitea/modules/webhook"
  24. "code.gitea.io/gitea/services/convert"
  25. "code.gitea.io/gitea/services/forms"
  26. webhook_service "code.gitea.io/gitea/services/webhook"
  27. )
  28. const (
  29. tplHooks base.TplName = "repo/settings/webhook/base"
  30. tplHookNew base.TplName = "repo/settings/webhook/new"
  31. tplOrgHookNew base.TplName = "org/settings/hook_new"
  32. tplAdminHookNew base.TplName = "admin/hook_new"
  33. )
  34. // Webhooks render web hooks list page
  35. func Webhooks(ctx *context.Context) {
  36. ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
  37. ctx.Data["PageIsSettingsHooks"] = true
  38. ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
  39. ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
  40. ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")
  41. ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
  42. if err != nil {
  43. ctx.ServerError("GetWebhooksByRepoID", err)
  44. return
  45. }
  46. ctx.Data["Webhooks"] = ws
  47. ctx.HTML(http.StatusOK, tplHooks)
  48. }
  49. type orgRepoCtx struct {
  50. OrgID int64
  51. RepoID int64
  52. IsAdmin bool
  53. IsSystemWebhook bool
  54. Link string
  55. LinkNew string
  56. NewTemplate base.TplName
  57. }
  58. // getOrgRepoCtx determines whether this is a repo, organization, or admin (both default and system) context.
  59. func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
  60. if len(ctx.Repo.RepoLink) > 0 {
  61. return &orgRepoCtx{
  62. RepoID: ctx.Repo.Repository.ID,
  63. Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
  64. LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
  65. NewTemplate: tplHookNew,
  66. }, nil
  67. }
  68. if len(ctx.Org.OrgLink) > 0 {
  69. return &orgRepoCtx{
  70. OrgID: ctx.Org.Organization.ID,
  71. Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
  72. LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
  73. NewTemplate: tplOrgHookNew,
  74. }, nil
  75. }
  76. if ctx.Doer.IsAdmin {
  77. // Are we looking at default webhooks?
  78. if ctx.Params(":configType") == "default-hooks" {
  79. return &orgRepoCtx{
  80. IsAdmin: true,
  81. Link: path.Join(setting.AppSubURL, "/admin/hooks"),
  82. LinkNew: path.Join(setting.AppSubURL, "/admin/default-hooks"),
  83. NewTemplate: tplAdminHookNew,
  84. }, nil
  85. }
  86. // Must be system webhooks instead
  87. return &orgRepoCtx{
  88. IsAdmin: true,
  89. IsSystemWebhook: true,
  90. Link: path.Join(setting.AppSubURL, "/admin/hooks"),
  91. LinkNew: path.Join(setting.AppSubURL, "/admin/system-hooks"),
  92. NewTemplate: tplAdminHookNew,
  93. }, nil
  94. }
  95. return nil, errors.New("unable to set OrgRepo context")
  96. }
  97. func checkHookType(ctx *context.Context) string {
  98. hookType := strings.ToLower(ctx.Params(":type"))
  99. if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
  100. ctx.NotFound("checkHookType", nil)
  101. return ""
  102. }
  103. return hookType
  104. }
  105. // WebhooksNew render creating webhook page
  106. func WebhooksNew(ctx *context.Context) {
  107. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  108. ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
  109. orCtx, err := getOrgRepoCtx(ctx)
  110. if err != nil {
  111. ctx.ServerError("getOrgRepoCtx", err)
  112. return
  113. }
  114. if orCtx.IsAdmin && orCtx.IsSystemWebhook {
  115. ctx.Data["PageIsAdminSystemHooks"] = true
  116. ctx.Data["PageIsAdminSystemHooksNew"] = true
  117. } else if orCtx.IsAdmin {
  118. ctx.Data["PageIsAdminDefaultHooks"] = true
  119. ctx.Data["PageIsAdminDefaultHooksNew"] = true
  120. } else {
  121. ctx.Data["PageIsSettingsHooks"] = true
  122. ctx.Data["PageIsSettingsHooksNew"] = true
  123. }
  124. hookType := checkHookType(ctx)
  125. ctx.Data["HookType"] = hookType
  126. if ctx.Written() {
  127. return
  128. }
  129. if hookType == "discord" {
  130. ctx.Data["DiscordHook"] = map[string]interface{}{
  131. "Username": "Gitea",
  132. }
  133. }
  134. ctx.Data["BaseLink"] = orCtx.LinkNew
  135. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  136. }
  137. // ParseHookEvent convert web form content to webhook.HookEvent
  138. func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
  139. return &webhook_module.HookEvent{
  140. PushOnly: form.PushOnly(),
  141. SendEverything: form.SendEverything(),
  142. ChooseEvents: form.ChooseEvents(),
  143. HookEvents: webhook_module.HookEvents{
  144. Create: form.Create,
  145. Delete: form.Delete,
  146. Fork: form.Fork,
  147. Issues: form.Issues,
  148. IssueAssign: form.IssueAssign,
  149. IssueLabel: form.IssueLabel,
  150. IssueMilestone: form.IssueMilestone,
  151. IssueComment: form.IssueComment,
  152. Release: form.Release,
  153. Push: form.Push,
  154. PullRequest: form.PullRequest,
  155. PullRequestAssign: form.PullRequestAssign,
  156. PullRequestLabel: form.PullRequestLabel,
  157. PullRequestMilestone: form.PullRequestMilestone,
  158. PullRequestComment: form.PullRequestComment,
  159. PullRequestReview: form.PullRequestReview,
  160. PullRequestSync: form.PullRequestSync,
  161. Wiki: form.Wiki,
  162. Repository: form.Repository,
  163. Package: form.Package,
  164. },
  165. BranchFilter: form.BranchFilter,
  166. }
  167. }
  168. type webhookParams struct {
  169. // Type should be imported from webhook package (webhook.XXX)
  170. Type string
  171. URL string
  172. ContentType webhook.HookContentType
  173. Secret string
  174. HTTPMethod string
  175. WebhookForm forms.WebhookForm
  176. Meta interface{}
  177. }
  178. func createWebhook(ctx *context.Context, params webhookParams) {
  179. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  180. ctx.Data["PageIsSettingsHooks"] = true
  181. ctx.Data["PageIsSettingsHooksNew"] = true
  182. ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook_module.HookEvent{}}
  183. ctx.Data["HookType"] = params.Type
  184. orCtx, err := getOrgRepoCtx(ctx)
  185. if err != nil {
  186. ctx.ServerError("getOrgRepoCtx", err)
  187. return
  188. }
  189. ctx.Data["BaseLink"] = orCtx.LinkNew
  190. if ctx.HasError() {
  191. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  192. return
  193. }
  194. var meta []byte
  195. if params.Meta != nil {
  196. meta, err = json.Marshal(params.Meta)
  197. if err != nil {
  198. ctx.ServerError("Marshal", err)
  199. return
  200. }
  201. }
  202. w := &webhook.Webhook{
  203. RepoID: orCtx.RepoID,
  204. URL: params.URL,
  205. HTTPMethod: params.HTTPMethod,
  206. ContentType: params.ContentType,
  207. Secret: params.Secret,
  208. HookEvent: ParseHookEvent(params.WebhookForm),
  209. IsActive: params.WebhookForm.Active,
  210. Type: params.Type,
  211. Meta: string(meta),
  212. OrgID: orCtx.OrgID,
  213. IsSystemWebhook: orCtx.IsSystemWebhook,
  214. }
  215. err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
  216. if err != nil {
  217. ctx.ServerError("SetHeaderAuthorization", err)
  218. return
  219. }
  220. if err := w.UpdateEvent(); err != nil {
  221. ctx.ServerError("UpdateEvent", err)
  222. return
  223. } else if err := webhook.CreateWebhook(ctx, w); err != nil {
  224. ctx.ServerError("CreateWebhook", err)
  225. return
  226. }
  227. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  228. ctx.Redirect(orCtx.Link)
  229. }
  230. func editWebhook(ctx *context.Context, params webhookParams) {
  231. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  232. ctx.Data["PageIsSettingsHooks"] = true
  233. ctx.Data["PageIsSettingsHooksEdit"] = true
  234. orCtx, w := checkWebhook(ctx)
  235. if ctx.Written() {
  236. return
  237. }
  238. ctx.Data["Webhook"] = w
  239. if ctx.HasError() {
  240. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  241. return
  242. }
  243. var meta []byte
  244. var err error
  245. if params.Meta != nil {
  246. meta, err = json.Marshal(params.Meta)
  247. if err != nil {
  248. ctx.ServerError("Marshal", err)
  249. return
  250. }
  251. }
  252. w.URL = params.URL
  253. w.ContentType = params.ContentType
  254. w.Secret = params.Secret
  255. w.HookEvent = ParseHookEvent(params.WebhookForm)
  256. w.IsActive = params.WebhookForm.Active
  257. w.HTTPMethod = params.HTTPMethod
  258. w.Meta = string(meta)
  259. err = w.SetHeaderAuthorization(params.WebhookForm.AuthorizationHeader)
  260. if err != nil {
  261. ctx.ServerError("SetHeaderAuthorization", err)
  262. return
  263. }
  264. if err := w.UpdateEvent(); err != nil {
  265. ctx.ServerError("UpdateEvent", err)
  266. return
  267. } else if err := webhook.UpdateWebhook(w); err != nil {
  268. ctx.ServerError("UpdateWebhook", err)
  269. return
  270. }
  271. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  272. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  273. }
  274. // GiteaHooksNewPost response for creating Gitea webhook
  275. func GiteaHooksNewPost(ctx *context.Context) {
  276. createWebhook(ctx, giteaHookParams(ctx))
  277. }
  278. // GiteaHooksEditPost response for editing Gitea webhook
  279. func GiteaHooksEditPost(ctx *context.Context) {
  280. editWebhook(ctx, giteaHookParams(ctx))
  281. }
  282. func giteaHookParams(ctx *context.Context) webhookParams {
  283. form := web.GetForm(ctx).(*forms.NewWebhookForm)
  284. contentType := webhook.ContentTypeJSON
  285. if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
  286. contentType = webhook.ContentTypeForm
  287. }
  288. return webhookParams{
  289. Type: webhook_module.GITEA,
  290. URL: form.PayloadURL,
  291. ContentType: contentType,
  292. Secret: form.Secret,
  293. HTTPMethod: form.HTTPMethod,
  294. WebhookForm: form.WebhookForm,
  295. }
  296. }
  297. // GogsHooksNewPost response for creating Gogs webhook
  298. func GogsHooksNewPost(ctx *context.Context) {
  299. createWebhook(ctx, gogsHookParams(ctx))
  300. }
  301. // GogsHooksEditPost response for editing Gogs webhook
  302. func GogsHooksEditPost(ctx *context.Context) {
  303. editWebhook(ctx, gogsHookParams(ctx))
  304. }
  305. func gogsHookParams(ctx *context.Context) webhookParams {
  306. form := web.GetForm(ctx).(*forms.NewGogshookForm)
  307. contentType := webhook.ContentTypeJSON
  308. if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
  309. contentType = webhook.ContentTypeForm
  310. }
  311. return webhookParams{
  312. Type: webhook_module.GOGS,
  313. URL: form.PayloadURL,
  314. ContentType: contentType,
  315. Secret: form.Secret,
  316. WebhookForm: form.WebhookForm,
  317. }
  318. }
  319. // DiscordHooksNewPost response for creating Discord webhook
  320. func DiscordHooksNewPost(ctx *context.Context) {
  321. createWebhook(ctx, discordHookParams(ctx))
  322. }
  323. // DiscordHooksEditPost response for editing Discord webhook
  324. func DiscordHooksEditPost(ctx *context.Context) {
  325. editWebhook(ctx, discordHookParams(ctx))
  326. }
  327. func discordHookParams(ctx *context.Context) webhookParams {
  328. form := web.GetForm(ctx).(*forms.NewDiscordHookForm)
  329. return webhookParams{
  330. Type: webhook_module.DISCORD,
  331. URL: form.PayloadURL,
  332. ContentType: webhook.ContentTypeJSON,
  333. WebhookForm: form.WebhookForm,
  334. Meta: &webhook_service.DiscordMeta{
  335. Username: form.Username,
  336. IconURL: form.IconURL,
  337. },
  338. }
  339. }
  340. // DingtalkHooksNewPost response for creating Dingtalk webhook
  341. func DingtalkHooksNewPost(ctx *context.Context) {
  342. createWebhook(ctx, dingtalkHookParams(ctx))
  343. }
  344. // DingtalkHooksEditPost response for editing Dingtalk webhook
  345. func DingtalkHooksEditPost(ctx *context.Context) {
  346. editWebhook(ctx, dingtalkHookParams(ctx))
  347. }
  348. func dingtalkHookParams(ctx *context.Context) webhookParams {
  349. form := web.GetForm(ctx).(*forms.NewDingtalkHookForm)
  350. return webhookParams{
  351. Type: webhook_module.DINGTALK,
  352. URL: form.PayloadURL,
  353. ContentType: webhook.ContentTypeJSON,
  354. WebhookForm: form.WebhookForm,
  355. }
  356. }
  357. // TelegramHooksNewPost response for creating Telegram webhook
  358. func TelegramHooksNewPost(ctx *context.Context) {
  359. createWebhook(ctx, telegramHookParams(ctx))
  360. }
  361. // TelegramHooksEditPost response for editing Telegram webhook
  362. func TelegramHooksEditPost(ctx *context.Context) {
  363. editWebhook(ctx, telegramHookParams(ctx))
  364. }
  365. func telegramHookParams(ctx *context.Context) webhookParams {
  366. form := web.GetForm(ctx).(*forms.NewTelegramHookForm)
  367. return webhookParams{
  368. Type: webhook_module.TELEGRAM,
  369. URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
  370. ContentType: webhook.ContentTypeJSON,
  371. WebhookForm: form.WebhookForm,
  372. Meta: &webhook_service.TelegramMeta{
  373. BotToken: form.BotToken,
  374. ChatID: form.ChatID,
  375. },
  376. }
  377. }
  378. // MatrixHooksNewPost response for creating Matrix webhook
  379. func MatrixHooksNewPost(ctx *context.Context) {
  380. createWebhook(ctx, matrixHookParams(ctx))
  381. }
  382. // MatrixHooksEditPost response for editing Matrix webhook
  383. func MatrixHooksEditPost(ctx *context.Context) {
  384. editWebhook(ctx, matrixHookParams(ctx))
  385. }
  386. func matrixHookParams(ctx *context.Context) webhookParams {
  387. form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
  388. return webhookParams{
  389. Type: webhook_module.MATRIX,
  390. URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
  391. ContentType: webhook.ContentTypeJSON,
  392. HTTPMethod: http.MethodPut,
  393. WebhookForm: form.WebhookForm,
  394. Meta: &webhook_service.MatrixMeta{
  395. HomeserverURL: form.HomeserverURL,
  396. Room: form.RoomID,
  397. MessageType: form.MessageType,
  398. },
  399. }
  400. }
  401. // MSTeamsHooksNewPost response for creating MSTeams webhook
  402. func MSTeamsHooksNewPost(ctx *context.Context) {
  403. createWebhook(ctx, mSTeamsHookParams(ctx))
  404. }
  405. // MSTeamsHooksEditPost response for editing MSTeams webhook
  406. func MSTeamsHooksEditPost(ctx *context.Context) {
  407. editWebhook(ctx, mSTeamsHookParams(ctx))
  408. }
  409. func mSTeamsHookParams(ctx *context.Context) webhookParams {
  410. form := web.GetForm(ctx).(*forms.NewMSTeamsHookForm)
  411. return webhookParams{
  412. Type: webhook_module.MSTEAMS,
  413. URL: form.PayloadURL,
  414. ContentType: webhook.ContentTypeJSON,
  415. WebhookForm: form.WebhookForm,
  416. }
  417. }
  418. // SlackHooksNewPost response for creating Slack webhook
  419. func SlackHooksNewPost(ctx *context.Context) {
  420. createWebhook(ctx, slackHookParams(ctx))
  421. }
  422. // SlackHooksEditPost response for editing Slack webhook
  423. func SlackHooksEditPost(ctx *context.Context) {
  424. editWebhook(ctx, slackHookParams(ctx))
  425. }
  426. func slackHookParams(ctx *context.Context) webhookParams {
  427. form := web.GetForm(ctx).(*forms.NewSlackHookForm)
  428. return webhookParams{
  429. Type: webhook_module.SLACK,
  430. URL: form.PayloadURL,
  431. ContentType: webhook.ContentTypeJSON,
  432. WebhookForm: form.WebhookForm,
  433. Meta: &webhook_service.SlackMeta{
  434. Channel: strings.TrimSpace(form.Channel),
  435. Username: form.Username,
  436. IconURL: form.IconURL,
  437. Color: form.Color,
  438. },
  439. }
  440. }
  441. // FeishuHooksNewPost response for creating Feishu webhook
  442. func FeishuHooksNewPost(ctx *context.Context) {
  443. createWebhook(ctx, feishuHookParams(ctx))
  444. }
  445. // FeishuHooksEditPost response for editing Feishu webhook
  446. func FeishuHooksEditPost(ctx *context.Context) {
  447. editWebhook(ctx, feishuHookParams(ctx))
  448. }
  449. func feishuHookParams(ctx *context.Context) webhookParams {
  450. form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
  451. return webhookParams{
  452. Type: webhook_module.FEISHU,
  453. URL: form.PayloadURL,
  454. ContentType: webhook.ContentTypeJSON,
  455. WebhookForm: form.WebhookForm,
  456. }
  457. }
  458. // WechatworkHooksNewPost response for creating Wechatwork webhook
  459. func WechatworkHooksNewPost(ctx *context.Context) {
  460. createWebhook(ctx, wechatworkHookParams(ctx))
  461. }
  462. // WechatworkHooksEditPost response for editing Wechatwork webhook
  463. func WechatworkHooksEditPost(ctx *context.Context) {
  464. editWebhook(ctx, wechatworkHookParams(ctx))
  465. }
  466. func wechatworkHookParams(ctx *context.Context) webhookParams {
  467. form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
  468. return webhookParams{
  469. Type: webhook_module.WECHATWORK,
  470. URL: form.PayloadURL,
  471. ContentType: webhook.ContentTypeJSON,
  472. WebhookForm: form.WebhookForm,
  473. }
  474. }
  475. // PackagistHooksNewPost response for creating Packagist webhook
  476. func PackagistHooksNewPost(ctx *context.Context) {
  477. createWebhook(ctx, packagistHookParams(ctx))
  478. }
  479. // PackagistHooksEditPost response for editing Packagist webhook
  480. func PackagistHooksEditPost(ctx *context.Context) {
  481. editWebhook(ctx, packagistHookParams(ctx))
  482. }
  483. func packagistHookParams(ctx *context.Context) webhookParams {
  484. form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
  485. return webhookParams{
  486. Type: webhook_module.PACKAGIST,
  487. URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
  488. ContentType: webhook.ContentTypeJSON,
  489. WebhookForm: form.WebhookForm,
  490. Meta: &webhook_service.PackagistMeta{
  491. Username: form.Username,
  492. APIToken: form.APIToken,
  493. PackageURL: form.PackageURL,
  494. },
  495. }
  496. }
  497. func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
  498. orCtx, err := getOrgRepoCtx(ctx)
  499. if err != nil {
  500. ctx.ServerError("getOrgRepoCtx", err)
  501. return nil, nil
  502. }
  503. ctx.Data["BaseLink"] = orCtx.Link
  504. var w *webhook.Webhook
  505. if orCtx.RepoID > 0 {
  506. w, err = webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
  507. } else if orCtx.OrgID > 0 {
  508. w, err = webhook.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
  509. } else if orCtx.IsAdmin {
  510. w, err = webhook.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id"))
  511. }
  512. if err != nil || w == nil {
  513. if webhook.IsErrWebhookNotExist(err) {
  514. ctx.NotFound("GetWebhookByID", nil)
  515. } else {
  516. ctx.ServerError("GetWebhookByID", err)
  517. }
  518. return nil, nil
  519. }
  520. ctx.Data["HookType"] = w.Type
  521. switch w.Type {
  522. case webhook_module.SLACK:
  523. ctx.Data["SlackHook"] = webhook_service.GetSlackHook(w)
  524. case webhook_module.DISCORD:
  525. ctx.Data["DiscordHook"] = webhook_service.GetDiscordHook(w)
  526. case webhook_module.TELEGRAM:
  527. ctx.Data["TelegramHook"] = webhook_service.GetTelegramHook(w)
  528. case webhook_module.MATRIX:
  529. ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
  530. case webhook_module.PACKAGIST:
  531. ctx.Data["PackagistHook"] = webhook_service.GetPackagistHook(w)
  532. }
  533. ctx.Data["History"], err = w.History(1)
  534. if err != nil {
  535. ctx.ServerError("History", err)
  536. }
  537. return orCtx, w
  538. }
  539. // WebHooksEdit render editing web hook page
  540. func WebHooksEdit(ctx *context.Context) {
  541. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  542. ctx.Data["PageIsSettingsHooks"] = true
  543. ctx.Data["PageIsSettingsHooksEdit"] = true
  544. orCtx, w := checkWebhook(ctx)
  545. if ctx.Written() {
  546. return
  547. }
  548. ctx.Data["Webhook"] = w
  549. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  550. }
  551. // TestWebhook test if web hook is work fine
  552. func TestWebhook(ctx *context.Context) {
  553. hookID := ctx.ParamsInt64(":id")
  554. w, err := webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
  555. if err != nil {
  556. ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
  557. ctx.Status(http.StatusInternalServerError)
  558. return
  559. }
  560. // Grab latest commit or fake one if it's empty repository.
  561. commit := ctx.Repo.Commit
  562. if commit == nil {
  563. ghost := user_model.NewGhostUser()
  564. commit = &git.Commit{
  565. ID: git.MustIDFromString(git.EmptySHA),
  566. Author: ghost.NewGitSig(),
  567. Committer: ghost.NewGitSig(),
  568. CommitMessage: "This is a fake commit",
  569. }
  570. }
  571. apiUser := convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone)
  572. apiCommit := &api.PayloadCommit{
  573. ID: commit.ID.String(),
  574. Message: commit.Message(),
  575. URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
  576. Author: &api.PayloadUser{
  577. Name: commit.Author.Name,
  578. Email: commit.Author.Email,
  579. },
  580. Committer: &api.PayloadUser{
  581. Name: commit.Committer.Name,
  582. Email: commit.Committer.Email,
  583. },
  584. }
  585. commitID := commit.ID.String()
  586. p := &api.PushPayload{
  587. Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
  588. Before: commitID,
  589. After: commitID,
  590. CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
  591. Commits: []*api.PayloadCommit{apiCommit},
  592. TotalCommits: 1,
  593. HeadCommit: apiCommit,
  594. Repo: convert.ToRepo(ctx, ctx.Repo.Repository, perm.AccessModeNone),
  595. Pusher: apiUser,
  596. Sender: apiUser,
  597. }
  598. if err := webhook_service.PrepareWebhook(ctx, w, webhook_module.HookEventPush, p); err != nil {
  599. ctx.Flash.Error("PrepareWebhook: " + err.Error())
  600. ctx.Status(http.StatusInternalServerError)
  601. } else {
  602. ctx.Flash.Info(ctx.Tr("repo.settings.webhook.delivery.success"))
  603. ctx.Status(http.StatusOK)
  604. }
  605. }
  606. // ReplayWebhook replays a webhook
  607. func ReplayWebhook(ctx *context.Context) {
  608. hookTaskUUID := ctx.Params(":uuid")
  609. orCtx, w := checkWebhook(ctx)
  610. if ctx.Written() {
  611. return
  612. }
  613. if err := webhook_service.ReplayHookTask(ctx, w, hookTaskUUID); err != nil {
  614. if webhook.IsErrHookTaskNotExist(err) {
  615. ctx.NotFound("ReplayHookTask", nil)
  616. } else {
  617. ctx.ServerError("ReplayHookTask", err)
  618. }
  619. return
  620. }
  621. ctx.Flash.Success(ctx.Tr("repo.settings.webhook.delivery.success"))
  622. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  623. }
  624. // DeleteWebhook delete a webhook
  625. func DeleteWebhook(ctx *context.Context) {
  626. if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
  627. ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
  628. } else {
  629. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  630. }
  631. ctx.JSON(http.StatusOK, map[string]interface{}{
  632. "redirect": ctx.Repo.RepoLink + "/settings/hooks",
  633. })
  634. }