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 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "errors"
  8. "fmt"
  9. "net/http"
  10. "path"
  11. "strings"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/convert"
  16. auth "code.gitea.io/gitea/modules/forms"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/setting"
  19. api "code.gitea.io/gitea/modules/structs"
  20. "code.gitea.io/gitea/modules/util"
  21. "code.gitea.io/gitea/modules/web"
  22. "code.gitea.io/gitea/services/webhook"
  23. jsoniter "github.com/json-iterator/go"
  24. )
  25. const (
  26. tplHooks base.TplName = "repo/settings/webhook/base"
  27. tplHookNew base.TplName = "repo/settings/webhook/new"
  28. tplOrgHookNew base.TplName = "org/settings/hook_new"
  29. tplAdminHookNew base.TplName = "admin/hook_new"
  30. )
  31. // Webhooks render web hooks list page
  32. func Webhooks(ctx *context.Context) {
  33. ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
  34. ctx.Data["PageIsSettingsHooks"] = true
  35. ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
  36. ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
  37. ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")
  38. ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, models.ListOptions{})
  39. if err != nil {
  40. ctx.ServerError("GetWebhooksByRepoID", err)
  41. return
  42. }
  43. ctx.Data["Webhooks"] = ws
  44. ctx.HTML(http.StatusOK, tplHooks)
  45. }
  46. type orgRepoCtx struct {
  47. OrgID int64
  48. RepoID int64
  49. IsAdmin bool
  50. IsSystemWebhook bool
  51. Link string
  52. LinkNew string
  53. NewTemplate base.TplName
  54. }
  55. // getOrgRepoCtx determines whether this is a repo, organization, or admin (both default and system) context.
  56. func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
  57. if len(ctx.Repo.RepoLink) > 0 {
  58. return &orgRepoCtx{
  59. RepoID: ctx.Repo.Repository.ID,
  60. Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
  61. LinkNew: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
  62. NewTemplate: tplHookNew,
  63. }, nil
  64. }
  65. if len(ctx.Org.OrgLink) > 0 {
  66. return &orgRepoCtx{
  67. OrgID: ctx.Org.Organization.ID,
  68. Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
  69. LinkNew: path.Join(ctx.Org.OrgLink, "settings/hooks"),
  70. NewTemplate: tplOrgHookNew,
  71. }, nil
  72. }
  73. if ctx.User.IsAdmin {
  74. // Are we looking at default webhooks?
  75. if ctx.Params(":configType") == "default-hooks" {
  76. return &orgRepoCtx{
  77. IsAdmin: true,
  78. Link: path.Join(setting.AppSubURL, "/admin/hooks"),
  79. LinkNew: path.Join(setting.AppSubURL, "/admin/default-hooks"),
  80. NewTemplate: tplAdminHookNew,
  81. }, nil
  82. }
  83. // Must be system webhooks instead
  84. return &orgRepoCtx{
  85. IsAdmin: true,
  86. IsSystemWebhook: true,
  87. Link: path.Join(setting.AppSubURL, "/admin/hooks"),
  88. LinkNew: path.Join(setting.AppSubURL, "/admin/system-hooks"),
  89. NewTemplate: tplAdminHookNew,
  90. }, nil
  91. }
  92. return nil, errors.New("Unable to set OrgRepo context")
  93. }
  94. func checkHookType(ctx *context.Context) string {
  95. hookType := strings.ToLower(ctx.Params(":type"))
  96. if !util.IsStringInSlice(hookType, setting.Webhook.Types, true) {
  97. ctx.NotFound("checkHookType", nil)
  98. return ""
  99. }
  100. return hookType
  101. }
  102. // WebhooksNew render creating webhook page
  103. func WebhooksNew(ctx *context.Context) {
  104. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  105. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  106. orCtx, err := getOrgRepoCtx(ctx)
  107. if err != nil {
  108. ctx.ServerError("getOrgRepoCtx", err)
  109. return
  110. }
  111. if orCtx.IsAdmin && orCtx.IsSystemWebhook {
  112. ctx.Data["PageIsAdminSystemHooks"] = true
  113. ctx.Data["PageIsAdminSystemHooksNew"] = true
  114. } else if orCtx.IsAdmin {
  115. ctx.Data["PageIsAdminDefaultHooks"] = true
  116. ctx.Data["PageIsAdminDefaultHooksNew"] = true
  117. } else {
  118. ctx.Data["PageIsSettingsHooks"] = true
  119. ctx.Data["PageIsSettingsHooksNew"] = true
  120. }
  121. hookType := checkHookType(ctx)
  122. ctx.Data["HookType"] = hookType
  123. if ctx.Written() {
  124. return
  125. }
  126. if hookType == "discord" {
  127. ctx.Data["DiscordHook"] = map[string]interface{}{
  128. "Username": "Gitea",
  129. "IconURL": setting.AppURL + "img/favicon.png",
  130. }
  131. }
  132. ctx.Data["BaseLink"] = orCtx.LinkNew
  133. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  134. }
  135. // ParseHookEvent convert web form content to models.HookEvent
  136. func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
  137. return &models.HookEvent{
  138. PushOnly: form.PushOnly(),
  139. SendEverything: form.SendEverything(),
  140. ChooseEvents: form.ChooseEvents(),
  141. HookEvents: models.HookEvents{
  142. Create: form.Create,
  143. Delete: form.Delete,
  144. Fork: form.Fork,
  145. Issues: form.Issues,
  146. IssueAssign: form.IssueAssign,
  147. IssueLabel: form.IssueLabel,
  148. IssueMilestone: form.IssueMilestone,
  149. IssueComment: form.IssueComment,
  150. Release: form.Release,
  151. Push: form.Push,
  152. PullRequest: form.PullRequest,
  153. PullRequestAssign: form.PullRequestAssign,
  154. PullRequestLabel: form.PullRequestLabel,
  155. PullRequestMilestone: form.PullRequestMilestone,
  156. PullRequestComment: form.PullRequestComment,
  157. PullRequestReview: form.PullRequestReview,
  158. PullRequestSync: form.PullRequestSync,
  159. Repository: form.Repository,
  160. },
  161. BranchFilter: form.BranchFilter,
  162. }
  163. }
  164. // GiteaHooksNewPost response for creating Gitea webhook
  165. func GiteaHooksNewPost(ctx *context.Context) {
  166. form := web.GetForm(ctx).(*auth.NewWebhookForm)
  167. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  168. ctx.Data["PageIsSettingsHooks"] = true
  169. ctx.Data["PageIsSettingsHooksNew"] = true
  170. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  171. ctx.Data["HookType"] = models.GITEA
  172. orCtx, err := getOrgRepoCtx(ctx)
  173. if err != nil {
  174. ctx.ServerError("getOrgRepoCtx", err)
  175. return
  176. }
  177. ctx.Data["BaseLink"] = orCtx.LinkNew
  178. if ctx.HasError() {
  179. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  180. return
  181. }
  182. contentType := models.ContentTypeJSON
  183. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  184. contentType = models.ContentTypeForm
  185. }
  186. w := &models.Webhook{
  187. RepoID: orCtx.RepoID,
  188. URL: form.PayloadURL,
  189. HTTPMethod: form.HTTPMethod,
  190. ContentType: contentType,
  191. Secret: form.Secret,
  192. HookEvent: ParseHookEvent(form.WebhookForm),
  193. IsActive: form.Active,
  194. Type: models.GITEA,
  195. OrgID: orCtx.OrgID,
  196. IsSystemWebhook: orCtx.IsSystemWebhook,
  197. }
  198. if err := w.UpdateEvent(); err != nil {
  199. ctx.ServerError("UpdateEvent", err)
  200. return
  201. } else if err := models.CreateWebhook(w); err != nil {
  202. ctx.ServerError("CreateWebhook", err)
  203. return
  204. }
  205. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  206. ctx.Redirect(orCtx.Link)
  207. }
  208. // GogsHooksNewPost response for creating webhook
  209. func GogsHooksNewPost(ctx *context.Context) {
  210. form := web.GetForm(ctx).(*auth.NewGogshookForm)
  211. newGogsWebhookPost(ctx, *form, models.GOGS)
  212. }
  213. // newGogsWebhookPost response for creating gogs hook
  214. func newGogsWebhookPost(ctx *context.Context, form auth.NewGogshookForm, kind models.HookTaskType) {
  215. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  216. ctx.Data["PageIsSettingsHooks"] = true
  217. ctx.Data["PageIsSettingsHooksNew"] = true
  218. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  219. ctx.Data["HookType"] = models.GOGS
  220. orCtx, err := getOrgRepoCtx(ctx)
  221. if err != nil {
  222. ctx.ServerError("getOrgRepoCtx", err)
  223. return
  224. }
  225. ctx.Data["BaseLink"] = orCtx.LinkNew
  226. if ctx.HasError() {
  227. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  228. return
  229. }
  230. contentType := models.ContentTypeJSON
  231. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  232. contentType = models.ContentTypeForm
  233. }
  234. w := &models.Webhook{
  235. RepoID: orCtx.RepoID,
  236. URL: form.PayloadURL,
  237. ContentType: contentType,
  238. Secret: form.Secret,
  239. HookEvent: ParseHookEvent(form.WebhookForm),
  240. IsActive: form.Active,
  241. Type: kind,
  242. OrgID: orCtx.OrgID,
  243. IsSystemWebhook: orCtx.IsSystemWebhook,
  244. }
  245. if err := w.UpdateEvent(); err != nil {
  246. ctx.ServerError("UpdateEvent", err)
  247. return
  248. } else if err := models.CreateWebhook(w); err != nil {
  249. ctx.ServerError("CreateWebhook", err)
  250. return
  251. }
  252. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  253. ctx.Redirect(orCtx.Link)
  254. }
  255. // DiscordHooksNewPost response for creating discord hook
  256. func DiscordHooksNewPost(ctx *context.Context) {
  257. form := web.GetForm(ctx).(*auth.NewDiscordHookForm)
  258. ctx.Data["Title"] = ctx.Tr("repo.settings")
  259. ctx.Data["PageIsSettingsHooks"] = true
  260. ctx.Data["PageIsSettingsHooksNew"] = true
  261. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  262. ctx.Data["HookType"] = models.DISCORD
  263. orCtx, err := getOrgRepoCtx(ctx)
  264. if err != nil {
  265. ctx.ServerError("getOrgRepoCtx", err)
  266. return
  267. }
  268. if ctx.HasError() {
  269. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  270. return
  271. }
  272. json := jsoniter.ConfigCompatibleWithStandardLibrary
  273. meta, err := json.Marshal(&webhook.DiscordMeta{
  274. Username: form.Username,
  275. IconURL: form.IconURL,
  276. })
  277. if err != nil {
  278. ctx.ServerError("Marshal", err)
  279. return
  280. }
  281. w := &models.Webhook{
  282. RepoID: orCtx.RepoID,
  283. URL: form.PayloadURL,
  284. ContentType: models.ContentTypeJSON,
  285. HookEvent: ParseHookEvent(form.WebhookForm),
  286. IsActive: form.Active,
  287. Type: models.DISCORD,
  288. Meta: string(meta),
  289. OrgID: orCtx.OrgID,
  290. IsSystemWebhook: orCtx.IsSystemWebhook,
  291. }
  292. if err := w.UpdateEvent(); err != nil {
  293. ctx.ServerError("UpdateEvent", err)
  294. return
  295. } else if err := models.CreateWebhook(w); err != nil {
  296. ctx.ServerError("CreateWebhook", err)
  297. return
  298. }
  299. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  300. ctx.Redirect(orCtx.Link)
  301. }
  302. // DingtalkHooksNewPost response for creating dingtalk hook
  303. func DingtalkHooksNewPost(ctx *context.Context) {
  304. form := web.GetForm(ctx).(*auth.NewDingtalkHookForm)
  305. ctx.Data["Title"] = ctx.Tr("repo.settings")
  306. ctx.Data["PageIsSettingsHooks"] = true
  307. ctx.Data["PageIsSettingsHooksNew"] = true
  308. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  309. ctx.Data["HookType"] = models.DINGTALK
  310. orCtx, err := getOrgRepoCtx(ctx)
  311. if err != nil {
  312. ctx.ServerError("getOrgRepoCtx", err)
  313. return
  314. }
  315. if ctx.HasError() {
  316. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  317. return
  318. }
  319. w := &models.Webhook{
  320. RepoID: orCtx.RepoID,
  321. URL: form.PayloadURL,
  322. ContentType: models.ContentTypeJSON,
  323. HookEvent: ParseHookEvent(form.WebhookForm),
  324. IsActive: form.Active,
  325. Type: models.DINGTALK,
  326. Meta: "",
  327. OrgID: orCtx.OrgID,
  328. IsSystemWebhook: orCtx.IsSystemWebhook,
  329. }
  330. if err := w.UpdateEvent(); err != nil {
  331. ctx.ServerError("UpdateEvent", err)
  332. return
  333. } else if err := models.CreateWebhook(w); err != nil {
  334. ctx.ServerError("CreateWebhook", err)
  335. return
  336. }
  337. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  338. ctx.Redirect(orCtx.Link)
  339. }
  340. // TelegramHooksNewPost response for creating telegram hook
  341. func TelegramHooksNewPost(ctx *context.Context) {
  342. form := web.GetForm(ctx).(*auth.NewTelegramHookForm)
  343. ctx.Data["Title"] = ctx.Tr("repo.settings")
  344. ctx.Data["PageIsSettingsHooks"] = true
  345. ctx.Data["PageIsSettingsHooksNew"] = true
  346. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  347. ctx.Data["HookType"] = models.TELEGRAM
  348. orCtx, err := getOrgRepoCtx(ctx)
  349. if err != nil {
  350. ctx.ServerError("getOrgRepoCtx", err)
  351. return
  352. }
  353. if ctx.HasError() {
  354. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  355. return
  356. }
  357. json := jsoniter.ConfigCompatibleWithStandardLibrary
  358. meta, err := json.Marshal(&webhook.TelegramMeta{
  359. BotToken: form.BotToken,
  360. ChatID: form.ChatID,
  361. })
  362. if err != nil {
  363. ctx.ServerError("Marshal", err)
  364. return
  365. }
  366. w := &models.Webhook{
  367. RepoID: orCtx.RepoID,
  368. URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
  369. ContentType: models.ContentTypeJSON,
  370. HookEvent: ParseHookEvent(form.WebhookForm),
  371. IsActive: form.Active,
  372. Type: models.TELEGRAM,
  373. Meta: string(meta),
  374. OrgID: orCtx.OrgID,
  375. IsSystemWebhook: orCtx.IsSystemWebhook,
  376. }
  377. if err := w.UpdateEvent(); err != nil {
  378. ctx.ServerError("UpdateEvent", err)
  379. return
  380. } else if err := models.CreateWebhook(w); err != nil {
  381. ctx.ServerError("CreateWebhook", err)
  382. return
  383. }
  384. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  385. ctx.Redirect(orCtx.Link)
  386. }
  387. // MatrixHooksNewPost response for creating a Matrix hook
  388. func MatrixHooksNewPost(ctx *context.Context) {
  389. form := web.GetForm(ctx).(*auth.NewMatrixHookForm)
  390. ctx.Data["Title"] = ctx.Tr("repo.settings")
  391. ctx.Data["PageIsSettingsHooks"] = true
  392. ctx.Data["PageIsSettingsHooksNew"] = true
  393. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  394. ctx.Data["HookType"] = models.MATRIX
  395. orCtx, err := getOrgRepoCtx(ctx)
  396. if err != nil {
  397. ctx.ServerError("getOrgRepoCtx", err)
  398. return
  399. }
  400. if ctx.HasError() {
  401. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  402. return
  403. }
  404. json := jsoniter.ConfigCompatibleWithStandardLibrary
  405. meta, err := json.Marshal(&webhook.MatrixMeta{
  406. HomeserverURL: form.HomeserverURL,
  407. Room: form.RoomID,
  408. AccessToken: form.AccessToken,
  409. MessageType: form.MessageType,
  410. })
  411. if err != nil {
  412. ctx.ServerError("Marshal", err)
  413. return
  414. }
  415. w := &models.Webhook{
  416. RepoID: orCtx.RepoID,
  417. URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
  418. ContentType: models.ContentTypeJSON,
  419. HTTPMethod: "PUT",
  420. HookEvent: ParseHookEvent(form.WebhookForm),
  421. IsActive: form.Active,
  422. Type: models.MATRIX,
  423. Meta: string(meta),
  424. OrgID: orCtx.OrgID,
  425. IsSystemWebhook: orCtx.IsSystemWebhook,
  426. }
  427. if err := w.UpdateEvent(); err != nil {
  428. ctx.ServerError("UpdateEvent", err)
  429. return
  430. } else if err := models.CreateWebhook(w); err != nil {
  431. ctx.ServerError("CreateWebhook", err)
  432. return
  433. }
  434. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  435. ctx.Redirect(orCtx.Link)
  436. }
  437. // MSTeamsHooksNewPost response for creating MS Teams hook
  438. func MSTeamsHooksNewPost(ctx *context.Context) {
  439. form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm)
  440. ctx.Data["Title"] = ctx.Tr("repo.settings")
  441. ctx.Data["PageIsSettingsHooks"] = true
  442. ctx.Data["PageIsSettingsHooksNew"] = true
  443. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  444. ctx.Data["HookType"] = models.MSTEAMS
  445. orCtx, err := getOrgRepoCtx(ctx)
  446. if err != nil {
  447. ctx.ServerError("getOrgRepoCtx", err)
  448. return
  449. }
  450. if ctx.HasError() {
  451. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  452. return
  453. }
  454. w := &models.Webhook{
  455. RepoID: orCtx.RepoID,
  456. URL: form.PayloadURL,
  457. ContentType: models.ContentTypeJSON,
  458. HookEvent: ParseHookEvent(form.WebhookForm),
  459. IsActive: form.Active,
  460. Type: models.MSTEAMS,
  461. Meta: "",
  462. OrgID: orCtx.OrgID,
  463. IsSystemWebhook: orCtx.IsSystemWebhook,
  464. }
  465. if err := w.UpdateEvent(); err != nil {
  466. ctx.ServerError("UpdateEvent", err)
  467. return
  468. } else if err := models.CreateWebhook(w); err != nil {
  469. ctx.ServerError("CreateWebhook", err)
  470. return
  471. }
  472. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  473. ctx.Redirect(orCtx.Link)
  474. }
  475. // SlackHooksNewPost response for creating slack hook
  476. func SlackHooksNewPost(ctx *context.Context) {
  477. form := web.GetForm(ctx).(*auth.NewSlackHookForm)
  478. ctx.Data["Title"] = ctx.Tr("repo.settings")
  479. ctx.Data["PageIsSettingsHooks"] = true
  480. ctx.Data["PageIsSettingsHooksNew"] = true
  481. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  482. ctx.Data["HookType"] = models.SLACK
  483. orCtx, err := getOrgRepoCtx(ctx)
  484. if err != nil {
  485. ctx.ServerError("getOrgRepoCtx", err)
  486. return
  487. }
  488. if ctx.HasError() {
  489. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  490. return
  491. }
  492. if form.HasInvalidChannel() {
  493. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  494. ctx.Redirect(orCtx.LinkNew + "/slack/new")
  495. return
  496. }
  497. json := jsoniter.ConfigCompatibleWithStandardLibrary
  498. meta, err := json.Marshal(&webhook.SlackMeta{
  499. Channel: strings.TrimSpace(form.Channel),
  500. Username: form.Username,
  501. IconURL: form.IconURL,
  502. Color: form.Color,
  503. })
  504. if err != nil {
  505. ctx.ServerError("Marshal", err)
  506. return
  507. }
  508. w := &models.Webhook{
  509. RepoID: orCtx.RepoID,
  510. URL: form.PayloadURL,
  511. ContentType: models.ContentTypeJSON,
  512. HookEvent: ParseHookEvent(form.WebhookForm),
  513. IsActive: form.Active,
  514. Type: models.SLACK,
  515. Meta: string(meta),
  516. OrgID: orCtx.OrgID,
  517. IsSystemWebhook: orCtx.IsSystemWebhook,
  518. }
  519. if err := w.UpdateEvent(); err != nil {
  520. ctx.ServerError("UpdateEvent", err)
  521. return
  522. } else if err := models.CreateWebhook(w); err != nil {
  523. ctx.ServerError("CreateWebhook", err)
  524. return
  525. }
  526. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  527. ctx.Redirect(orCtx.Link)
  528. }
  529. // FeishuHooksNewPost response for creating feishu hook
  530. func FeishuHooksNewPost(ctx *context.Context) {
  531. form := web.GetForm(ctx).(*auth.NewFeishuHookForm)
  532. ctx.Data["Title"] = ctx.Tr("repo.settings")
  533. ctx.Data["PageIsSettingsHooks"] = true
  534. ctx.Data["PageIsSettingsHooksNew"] = true
  535. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  536. ctx.Data["HookType"] = models.FEISHU
  537. orCtx, err := getOrgRepoCtx(ctx)
  538. if err != nil {
  539. ctx.ServerError("getOrgRepoCtx", err)
  540. return
  541. }
  542. if ctx.HasError() {
  543. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  544. return
  545. }
  546. w := &models.Webhook{
  547. RepoID: orCtx.RepoID,
  548. URL: form.PayloadURL,
  549. ContentType: models.ContentTypeJSON,
  550. HookEvent: ParseHookEvent(form.WebhookForm),
  551. IsActive: form.Active,
  552. Type: models.FEISHU,
  553. Meta: "",
  554. OrgID: orCtx.OrgID,
  555. IsSystemWebhook: orCtx.IsSystemWebhook,
  556. }
  557. if err := w.UpdateEvent(); err != nil {
  558. ctx.ServerError("UpdateEvent", err)
  559. return
  560. } else if err := models.CreateWebhook(w); err != nil {
  561. ctx.ServerError("CreateWebhook", err)
  562. return
  563. }
  564. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  565. ctx.Redirect(orCtx.Link)
  566. }
  567. func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
  568. ctx.Data["RequireHighlightJS"] = true
  569. orCtx, err := getOrgRepoCtx(ctx)
  570. if err != nil {
  571. ctx.ServerError("getOrgRepoCtx", err)
  572. return nil, nil
  573. }
  574. ctx.Data["BaseLink"] = orCtx.Link
  575. var w *models.Webhook
  576. if orCtx.RepoID > 0 {
  577. w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
  578. } else if orCtx.OrgID > 0 {
  579. w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
  580. } else if orCtx.IsAdmin {
  581. w, err = models.GetSystemOrDefaultWebhook(ctx.ParamsInt64(":id"))
  582. }
  583. if err != nil || w == nil {
  584. if models.IsErrWebhookNotExist(err) {
  585. ctx.NotFound("GetWebhookByID", nil)
  586. } else {
  587. ctx.ServerError("GetWebhookByID", err)
  588. }
  589. return nil, nil
  590. }
  591. ctx.Data["HookType"] = w.Type
  592. switch w.Type {
  593. case models.SLACK:
  594. ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
  595. case models.DISCORD:
  596. ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
  597. case models.TELEGRAM:
  598. ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
  599. case models.MATRIX:
  600. ctx.Data["MatrixHook"] = webhook.GetMatrixHook(w)
  601. }
  602. ctx.Data["History"], err = w.History(1)
  603. if err != nil {
  604. ctx.ServerError("History", err)
  605. }
  606. return orCtx, w
  607. }
  608. // WebHooksEdit render editing web hook page
  609. func WebHooksEdit(ctx *context.Context) {
  610. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  611. ctx.Data["PageIsSettingsHooks"] = true
  612. ctx.Data["PageIsSettingsHooksEdit"] = true
  613. orCtx, w := checkWebhook(ctx)
  614. if ctx.Written() {
  615. return
  616. }
  617. ctx.Data["Webhook"] = w
  618. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  619. }
  620. // WebHooksEditPost response for editing web hook
  621. func WebHooksEditPost(ctx *context.Context) {
  622. form := web.GetForm(ctx).(*auth.NewWebhookForm)
  623. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  624. ctx.Data["PageIsSettingsHooks"] = true
  625. ctx.Data["PageIsSettingsHooksEdit"] = true
  626. orCtx, w := checkWebhook(ctx)
  627. if ctx.Written() {
  628. return
  629. }
  630. ctx.Data["Webhook"] = w
  631. if ctx.HasError() {
  632. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  633. return
  634. }
  635. contentType := models.ContentTypeJSON
  636. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  637. contentType = models.ContentTypeForm
  638. }
  639. w.URL = form.PayloadURL
  640. w.ContentType = contentType
  641. w.Secret = form.Secret
  642. w.HookEvent = ParseHookEvent(form.WebhookForm)
  643. w.IsActive = form.Active
  644. w.HTTPMethod = form.HTTPMethod
  645. if err := w.UpdateEvent(); err != nil {
  646. ctx.ServerError("UpdateEvent", err)
  647. return
  648. } else if err := models.UpdateWebhook(w); err != nil {
  649. ctx.ServerError("WebHooksEditPost", err)
  650. return
  651. }
  652. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  653. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  654. }
  655. // GogsHooksEditPost response for editing gogs hook
  656. func GogsHooksEditPost(ctx *context.Context) {
  657. form := web.GetForm(ctx).(*auth.NewGogshookForm)
  658. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  659. ctx.Data["PageIsSettingsHooks"] = true
  660. ctx.Data["PageIsSettingsHooksEdit"] = true
  661. orCtx, w := checkWebhook(ctx)
  662. if ctx.Written() {
  663. return
  664. }
  665. ctx.Data["Webhook"] = w
  666. if ctx.HasError() {
  667. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  668. return
  669. }
  670. contentType := models.ContentTypeJSON
  671. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  672. contentType = models.ContentTypeForm
  673. }
  674. w.URL = form.PayloadURL
  675. w.ContentType = contentType
  676. w.Secret = form.Secret
  677. w.HookEvent = ParseHookEvent(form.WebhookForm)
  678. w.IsActive = form.Active
  679. if err := w.UpdateEvent(); err != nil {
  680. ctx.ServerError("UpdateEvent", err)
  681. return
  682. } else if err := models.UpdateWebhook(w); err != nil {
  683. ctx.ServerError("GogsHooksEditPost", err)
  684. return
  685. }
  686. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  687. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  688. }
  689. // SlackHooksEditPost response for editing slack hook
  690. func SlackHooksEditPost(ctx *context.Context) {
  691. form := web.GetForm(ctx).(*auth.NewSlackHookForm)
  692. ctx.Data["Title"] = ctx.Tr("repo.settings")
  693. ctx.Data["PageIsSettingsHooks"] = true
  694. ctx.Data["PageIsSettingsHooksEdit"] = true
  695. orCtx, w := checkWebhook(ctx)
  696. if ctx.Written() {
  697. return
  698. }
  699. ctx.Data["Webhook"] = w
  700. if ctx.HasError() {
  701. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  702. return
  703. }
  704. if form.HasInvalidChannel() {
  705. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  706. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  707. return
  708. }
  709. json := jsoniter.ConfigCompatibleWithStandardLibrary
  710. meta, err := json.Marshal(&webhook.SlackMeta{
  711. Channel: strings.TrimSpace(form.Channel),
  712. Username: form.Username,
  713. IconURL: form.IconURL,
  714. Color: form.Color,
  715. })
  716. if err != nil {
  717. ctx.ServerError("Marshal", err)
  718. return
  719. }
  720. w.URL = form.PayloadURL
  721. w.Meta = string(meta)
  722. w.HookEvent = ParseHookEvent(form.WebhookForm)
  723. w.IsActive = form.Active
  724. if err := w.UpdateEvent(); err != nil {
  725. ctx.ServerError("UpdateEvent", err)
  726. return
  727. } else if err := models.UpdateWebhook(w); err != nil {
  728. ctx.ServerError("UpdateWebhook", err)
  729. return
  730. }
  731. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  732. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  733. }
  734. // DiscordHooksEditPost response for editing discord hook
  735. func DiscordHooksEditPost(ctx *context.Context) {
  736. form := web.GetForm(ctx).(*auth.NewDiscordHookForm)
  737. ctx.Data["Title"] = ctx.Tr("repo.settings")
  738. ctx.Data["PageIsSettingsHooks"] = true
  739. ctx.Data["PageIsSettingsHooksEdit"] = true
  740. orCtx, w := checkWebhook(ctx)
  741. if ctx.Written() {
  742. return
  743. }
  744. ctx.Data["Webhook"] = w
  745. if ctx.HasError() {
  746. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  747. return
  748. }
  749. json := jsoniter.ConfigCompatibleWithStandardLibrary
  750. meta, err := json.Marshal(&webhook.DiscordMeta{
  751. Username: form.Username,
  752. IconURL: form.IconURL,
  753. })
  754. if err != nil {
  755. ctx.ServerError("Marshal", err)
  756. return
  757. }
  758. w.URL = form.PayloadURL
  759. w.Meta = string(meta)
  760. w.HookEvent = ParseHookEvent(form.WebhookForm)
  761. w.IsActive = form.Active
  762. if err := w.UpdateEvent(); err != nil {
  763. ctx.ServerError("UpdateEvent", err)
  764. return
  765. } else if err := models.UpdateWebhook(w); err != nil {
  766. ctx.ServerError("UpdateWebhook", err)
  767. return
  768. }
  769. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  770. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  771. }
  772. // DingtalkHooksEditPost response for editing discord hook
  773. func DingtalkHooksEditPost(ctx *context.Context) {
  774. form := web.GetForm(ctx).(*auth.NewDingtalkHookForm)
  775. ctx.Data["Title"] = ctx.Tr("repo.settings")
  776. ctx.Data["PageIsSettingsHooks"] = true
  777. ctx.Data["PageIsSettingsHooksEdit"] = true
  778. orCtx, w := checkWebhook(ctx)
  779. if ctx.Written() {
  780. return
  781. }
  782. ctx.Data["Webhook"] = w
  783. if ctx.HasError() {
  784. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  785. return
  786. }
  787. w.URL = form.PayloadURL
  788. w.HookEvent = ParseHookEvent(form.WebhookForm)
  789. w.IsActive = form.Active
  790. if err := w.UpdateEvent(); err != nil {
  791. ctx.ServerError("UpdateEvent", err)
  792. return
  793. } else if err := models.UpdateWebhook(w); err != nil {
  794. ctx.ServerError("UpdateWebhook", err)
  795. return
  796. }
  797. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  798. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  799. }
  800. // TelegramHooksEditPost response for editing discord hook
  801. func TelegramHooksEditPost(ctx *context.Context) {
  802. form := web.GetForm(ctx).(*auth.NewTelegramHookForm)
  803. ctx.Data["Title"] = ctx.Tr("repo.settings")
  804. ctx.Data["PageIsSettingsHooks"] = true
  805. ctx.Data["PageIsSettingsHooksEdit"] = true
  806. orCtx, w := checkWebhook(ctx)
  807. if ctx.Written() {
  808. return
  809. }
  810. ctx.Data["Webhook"] = w
  811. if ctx.HasError() {
  812. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  813. return
  814. }
  815. json := jsoniter.ConfigCompatibleWithStandardLibrary
  816. meta, err := json.Marshal(&webhook.TelegramMeta{
  817. BotToken: form.BotToken,
  818. ChatID: form.ChatID,
  819. })
  820. if err != nil {
  821. ctx.ServerError("Marshal", err)
  822. return
  823. }
  824. w.Meta = string(meta)
  825. w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
  826. w.HookEvent = ParseHookEvent(form.WebhookForm)
  827. w.IsActive = form.Active
  828. if err := w.UpdateEvent(); err != nil {
  829. ctx.ServerError("UpdateEvent", err)
  830. return
  831. } else if err := models.UpdateWebhook(w); err != nil {
  832. ctx.ServerError("UpdateWebhook", err)
  833. return
  834. }
  835. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  836. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  837. }
  838. // MatrixHooksEditPost response for editing a Matrix hook
  839. func MatrixHooksEditPost(ctx *context.Context) {
  840. form := web.GetForm(ctx).(*auth.NewMatrixHookForm)
  841. ctx.Data["Title"] = ctx.Tr("repo.settings")
  842. ctx.Data["PageIsSettingsHooks"] = true
  843. ctx.Data["PageIsSettingsHooksEdit"] = true
  844. orCtx, w := checkWebhook(ctx)
  845. if ctx.Written() {
  846. return
  847. }
  848. ctx.Data["Webhook"] = w
  849. if ctx.HasError() {
  850. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  851. return
  852. }
  853. json := jsoniter.ConfigCompatibleWithStandardLibrary
  854. meta, err := json.Marshal(&webhook.MatrixMeta{
  855. HomeserverURL: form.HomeserverURL,
  856. Room: form.RoomID,
  857. AccessToken: form.AccessToken,
  858. MessageType: form.MessageType,
  859. })
  860. if err != nil {
  861. ctx.ServerError("Marshal", err)
  862. return
  863. }
  864. w.Meta = string(meta)
  865. w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID)
  866. w.HookEvent = ParseHookEvent(form.WebhookForm)
  867. w.IsActive = form.Active
  868. if err := w.UpdateEvent(); err != nil {
  869. ctx.ServerError("UpdateEvent", err)
  870. return
  871. } else if err := models.UpdateWebhook(w); err != nil {
  872. ctx.ServerError("UpdateWebhook", err)
  873. return
  874. }
  875. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  876. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  877. }
  878. // MSTeamsHooksEditPost response for editing MS Teams hook
  879. func MSTeamsHooksEditPost(ctx *context.Context) {
  880. form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm)
  881. ctx.Data["Title"] = ctx.Tr("repo.settings")
  882. ctx.Data["PageIsSettingsHooks"] = true
  883. ctx.Data["PageIsSettingsHooksEdit"] = true
  884. orCtx, w := checkWebhook(ctx)
  885. if ctx.Written() {
  886. return
  887. }
  888. ctx.Data["Webhook"] = w
  889. if ctx.HasError() {
  890. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  891. return
  892. }
  893. w.URL = form.PayloadURL
  894. w.HookEvent = ParseHookEvent(form.WebhookForm)
  895. w.IsActive = form.Active
  896. if err := w.UpdateEvent(); err != nil {
  897. ctx.ServerError("UpdateEvent", err)
  898. return
  899. } else if err := models.UpdateWebhook(w); err != nil {
  900. ctx.ServerError("UpdateWebhook", err)
  901. return
  902. }
  903. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  904. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  905. }
  906. // FeishuHooksEditPost response for editing feishu hook
  907. func FeishuHooksEditPost(ctx *context.Context) {
  908. form := web.GetForm(ctx).(*auth.NewFeishuHookForm)
  909. ctx.Data["Title"] = ctx.Tr("repo.settings")
  910. ctx.Data["PageIsSettingsHooks"] = true
  911. ctx.Data["PageIsSettingsHooksEdit"] = true
  912. orCtx, w := checkWebhook(ctx)
  913. if ctx.Written() {
  914. return
  915. }
  916. ctx.Data["Webhook"] = w
  917. if ctx.HasError() {
  918. ctx.HTML(http.StatusOK, orCtx.NewTemplate)
  919. return
  920. }
  921. w.URL = form.PayloadURL
  922. w.HookEvent = ParseHookEvent(form.WebhookForm)
  923. w.IsActive = form.Active
  924. if err := w.UpdateEvent(); err != nil {
  925. ctx.ServerError("UpdateEvent", err)
  926. return
  927. } else if err := models.UpdateWebhook(w); err != nil {
  928. ctx.ServerError("UpdateWebhook", err)
  929. return
  930. }
  931. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  932. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  933. }
  934. // TestWebhook test if web hook is work fine
  935. func TestWebhook(ctx *context.Context) {
  936. hookID := ctx.ParamsInt64(":id")
  937. w, err := models.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
  938. if err != nil {
  939. ctx.Flash.Error("GetWebhookByID: " + err.Error())
  940. ctx.Status(500)
  941. return
  942. }
  943. // Grab latest commit or fake one if it's empty repository.
  944. commit := ctx.Repo.Commit
  945. if commit == nil {
  946. ghost := models.NewGhostUser()
  947. commit = &git.Commit{
  948. ID: git.MustIDFromString(git.EmptySHA),
  949. Author: ghost.NewGitSig(),
  950. Committer: ghost.NewGitSig(),
  951. CommitMessage: "This is a fake commit",
  952. }
  953. }
  954. apiUser := convert.ToUserWithAccessMode(ctx.User, models.AccessModeNone)
  955. p := &api.PushPayload{
  956. Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
  957. Before: commit.ID.String(),
  958. After: commit.ID.String(),
  959. Commits: []*api.PayloadCommit{
  960. {
  961. ID: commit.ID.String(),
  962. Message: commit.Message(),
  963. URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
  964. Author: &api.PayloadUser{
  965. Name: commit.Author.Name,
  966. Email: commit.Author.Email,
  967. },
  968. Committer: &api.PayloadUser{
  969. Name: commit.Committer.Name,
  970. Email: commit.Committer.Email,
  971. },
  972. },
  973. },
  974. Repo: convert.ToRepo(ctx.Repo.Repository, models.AccessModeNone),
  975. Pusher: apiUser,
  976. Sender: apiUser,
  977. }
  978. if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
  979. ctx.Flash.Error("PrepareWebhook: " + err.Error())
  980. ctx.Status(500)
  981. } else {
  982. ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
  983. ctx.Status(200)
  984. }
  985. }
  986. // DeleteWebhook delete a webhook
  987. func DeleteWebhook(ctx *context.Context) {
  988. if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
  989. ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
  990. } else {
  991. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  992. }
  993. ctx.JSON(http.StatusOK, map[string]interface{}{
  994. "redirect": ctx.Repo.RepoLink + "/settings/hooks",
  995. })
  996. }