You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

webhook.go 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  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. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "path"
  11. "strings"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/auth"
  14. "code.gitea.io/gitea/modules/base"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/git"
  17. "code.gitea.io/gitea/modules/setting"
  18. api "code.gitea.io/gitea/modules/structs"
  19. "code.gitea.io/gitea/modules/webhook"
  20. "github.com/unknwon/com"
  21. )
  22. const (
  23. tplHooks base.TplName = "repo/settings/webhook/base"
  24. tplHookNew base.TplName = "repo/settings/webhook/new"
  25. tplOrgHookNew base.TplName = "org/settings/hook_new"
  26. tplAdminHookNew base.TplName = "admin/hook_new"
  27. )
  28. // Webhooks render web hooks list page
  29. func Webhooks(ctx *context.Context) {
  30. ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
  31. ctx.Data["PageIsSettingsHooks"] = true
  32. ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks"
  33. ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")
  34. ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, models.ListOptions{})
  35. if err != nil {
  36. ctx.ServerError("GetWebhooksByRepoID", err)
  37. return
  38. }
  39. ctx.Data["Webhooks"] = ws
  40. ctx.HTML(200, tplHooks)
  41. }
  42. type orgRepoCtx struct {
  43. OrgID int64
  44. RepoID int64
  45. IsAdmin bool
  46. Link string
  47. NewTemplate base.TplName
  48. }
  49. // getOrgRepoCtx determines whether this is a repo, organization, or admin context.
  50. func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
  51. if len(ctx.Repo.RepoLink) > 0 {
  52. return &orgRepoCtx{
  53. RepoID: ctx.Repo.Repository.ID,
  54. Link: path.Join(ctx.Repo.RepoLink, "settings/hooks"),
  55. NewTemplate: tplHookNew,
  56. }, nil
  57. }
  58. if len(ctx.Org.OrgLink) > 0 {
  59. return &orgRepoCtx{
  60. OrgID: ctx.Org.Organization.ID,
  61. Link: path.Join(ctx.Org.OrgLink, "settings/hooks"),
  62. NewTemplate: tplOrgHookNew,
  63. }, nil
  64. }
  65. if ctx.User.IsAdmin {
  66. return &orgRepoCtx{
  67. IsAdmin: true,
  68. Link: path.Join(setting.AppSubURL, "/admin/hooks"),
  69. NewTemplate: tplAdminHookNew,
  70. }, nil
  71. }
  72. return nil, errors.New("Unable to set OrgRepo context")
  73. }
  74. func checkHookType(ctx *context.Context) string {
  75. hookType := strings.ToLower(ctx.Params(":type"))
  76. if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) {
  77. ctx.NotFound("checkHookType", nil)
  78. return ""
  79. }
  80. return hookType
  81. }
  82. // WebhooksNew render creating webhook page
  83. func WebhooksNew(ctx *context.Context) {
  84. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  85. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  86. orCtx, err := getOrgRepoCtx(ctx)
  87. if err != nil {
  88. ctx.ServerError("getOrgRepoCtx", err)
  89. return
  90. }
  91. if orCtx.IsAdmin {
  92. ctx.Data["PageIsAdminHooks"] = true
  93. ctx.Data["PageIsAdminHooksNew"] = true
  94. } else {
  95. ctx.Data["PageIsSettingsHooks"] = true
  96. ctx.Data["PageIsSettingsHooksNew"] = true
  97. }
  98. hookType := checkHookType(ctx)
  99. ctx.Data["HookType"] = hookType
  100. if ctx.Written() {
  101. return
  102. }
  103. if hookType == "discord" {
  104. ctx.Data["DiscordHook"] = map[string]interface{}{
  105. "Username": "Gitea",
  106. "IconURL": setting.AppURL + "img/favicon.png",
  107. }
  108. }
  109. ctx.Data["BaseLink"] = orCtx.Link
  110. ctx.HTML(200, orCtx.NewTemplate)
  111. }
  112. // ParseHookEvent convert web form content to models.HookEvent
  113. func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
  114. return &models.HookEvent{
  115. PushOnly: form.PushOnly(),
  116. SendEverything: form.SendEverything(),
  117. ChooseEvents: form.ChooseEvents(),
  118. HookEvents: models.HookEvents{
  119. Create: form.Create,
  120. Delete: form.Delete,
  121. Fork: form.Fork,
  122. Issues: form.Issues,
  123. IssueComment: form.IssueComment,
  124. Release: form.Release,
  125. Push: form.Push,
  126. PullRequest: form.PullRequest,
  127. Repository: form.Repository,
  128. },
  129. BranchFilter: form.BranchFilter,
  130. }
  131. }
  132. // WebHooksNewPost response for creating webhook
  133. func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
  134. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  135. ctx.Data["PageIsSettingsHooks"] = true
  136. ctx.Data["PageIsSettingsHooksNew"] = true
  137. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  138. ctx.Data["HookType"] = "gitea"
  139. orCtx, err := getOrgRepoCtx(ctx)
  140. if err != nil {
  141. ctx.ServerError("getOrgRepoCtx", err)
  142. return
  143. }
  144. ctx.Data["BaseLink"] = orCtx.Link
  145. if ctx.HasError() {
  146. ctx.HTML(200, orCtx.NewTemplate)
  147. return
  148. }
  149. contentType := models.ContentTypeJSON
  150. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  151. contentType = models.ContentTypeForm
  152. }
  153. w := &models.Webhook{
  154. RepoID: orCtx.RepoID,
  155. URL: form.PayloadURL,
  156. HTTPMethod: form.HTTPMethod,
  157. ContentType: contentType,
  158. Secret: form.Secret,
  159. HookEvent: ParseHookEvent(form.WebhookForm),
  160. IsActive: form.Active,
  161. HookTaskType: models.GITEA,
  162. OrgID: orCtx.OrgID,
  163. }
  164. if err := w.UpdateEvent(); err != nil {
  165. ctx.ServerError("UpdateEvent", err)
  166. return
  167. } else if err := models.CreateWebhook(w); err != nil {
  168. ctx.ServerError("CreateWebhook", err)
  169. return
  170. }
  171. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  172. ctx.Redirect(orCtx.Link)
  173. }
  174. // GogsHooksNewPost response for creating webhook
  175. func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) {
  176. newGogsWebhookPost(ctx, form, models.GOGS)
  177. }
  178. // newGogsWebhookPost response for creating gogs hook
  179. func newGogsWebhookPost(ctx *context.Context, form auth.NewGogshookForm, kind models.HookTaskType) {
  180. ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
  181. ctx.Data["PageIsSettingsHooks"] = true
  182. ctx.Data["PageIsSettingsHooksNew"] = true
  183. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  184. ctx.Data["HookType"] = "gogs"
  185. orCtx, err := getOrgRepoCtx(ctx)
  186. if err != nil {
  187. ctx.ServerError("getOrgRepoCtx", err)
  188. return
  189. }
  190. ctx.Data["BaseLink"] = orCtx.Link
  191. if ctx.HasError() {
  192. ctx.HTML(200, orCtx.NewTemplate)
  193. return
  194. }
  195. contentType := models.ContentTypeJSON
  196. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  197. contentType = models.ContentTypeForm
  198. }
  199. w := &models.Webhook{
  200. RepoID: orCtx.RepoID,
  201. URL: form.PayloadURL,
  202. ContentType: contentType,
  203. Secret: form.Secret,
  204. HookEvent: ParseHookEvent(form.WebhookForm),
  205. IsActive: form.Active,
  206. HookTaskType: kind,
  207. OrgID: orCtx.OrgID,
  208. }
  209. if err := w.UpdateEvent(); err != nil {
  210. ctx.ServerError("UpdateEvent", err)
  211. return
  212. } else if err := models.CreateWebhook(w); err != nil {
  213. ctx.ServerError("CreateWebhook", err)
  214. return
  215. }
  216. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  217. ctx.Redirect(orCtx.Link)
  218. }
  219. // DiscordHooksNewPost response for creating discord hook
  220. func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) {
  221. ctx.Data["Title"] = ctx.Tr("repo.settings")
  222. ctx.Data["PageIsSettingsHooks"] = true
  223. ctx.Data["PageIsSettingsHooksNew"] = true
  224. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  225. orCtx, err := getOrgRepoCtx(ctx)
  226. if err != nil {
  227. ctx.ServerError("getOrgRepoCtx", err)
  228. return
  229. }
  230. if ctx.HasError() {
  231. ctx.HTML(200, orCtx.NewTemplate)
  232. return
  233. }
  234. meta, err := json.Marshal(&webhook.DiscordMeta{
  235. Username: form.Username,
  236. IconURL: form.IconURL,
  237. })
  238. if err != nil {
  239. ctx.ServerError("Marshal", err)
  240. return
  241. }
  242. w := &models.Webhook{
  243. RepoID: orCtx.RepoID,
  244. URL: form.PayloadURL,
  245. ContentType: models.ContentTypeJSON,
  246. HookEvent: ParseHookEvent(form.WebhookForm),
  247. IsActive: form.Active,
  248. HookTaskType: models.DISCORD,
  249. Meta: string(meta),
  250. OrgID: orCtx.OrgID,
  251. }
  252. if err := w.UpdateEvent(); err != nil {
  253. ctx.ServerError("UpdateEvent", err)
  254. return
  255. } else if err := models.CreateWebhook(w); err != nil {
  256. ctx.ServerError("CreateWebhook", err)
  257. return
  258. }
  259. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  260. ctx.Redirect(orCtx.Link)
  261. }
  262. // DingtalkHooksNewPost response for creating dingtalk hook
  263. func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
  264. ctx.Data["Title"] = ctx.Tr("repo.settings")
  265. ctx.Data["PageIsSettingsHooks"] = true
  266. ctx.Data["PageIsSettingsHooksNew"] = true
  267. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  268. orCtx, err := getOrgRepoCtx(ctx)
  269. if err != nil {
  270. ctx.ServerError("getOrgRepoCtx", err)
  271. return
  272. }
  273. if ctx.HasError() {
  274. ctx.HTML(200, orCtx.NewTemplate)
  275. return
  276. }
  277. w := &models.Webhook{
  278. RepoID: orCtx.RepoID,
  279. URL: form.PayloadURL,
  280. ContentType: models.ContentTypeJSON,
  281. HookEvent: ParseHookEvent(form.WebhookForm),
  282. IsActive: form.Active,
  283. HookTaskType: models.DINGTALK,
  284. Meta: "",
  285. OrgID: orCtx.OrgID,
  286. }
  287. if err := w.UpdateEvent(); err != nil {
  288. ctx.ServerError("UpdateEvent", err)
  289. return
  290. } else if err := models.CreateWebhook(w); err != nil {
  291. ctx.ServerError("CreateWebhook", err)
  292. return
  293. }
  294. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  295. ctx.Redirect(orCtx.Link)
  296. }
  297. // TelegramHooksNewPost response for creating telegram hook
  298. func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) {
  299. ctx.Data["Title"] = ctx.Tr("repo.settings")
  300. ctx.Data["PageIsSettingsHooks"] = true
  301. ctx.Data["PageIsSettingsHooksNew"] = true
  302. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  303. orCtx, err := getOrgRepoCtx(ctx)
  304. if err != nil {
  305. ctx.ServerError("getOrgRepoCtx", err)
  306. return
  307. }
  308. if ctx.HasError() {
  309. ctx.HTML(200, orCtx.NewTemplate)
  310. return
  311. }
  312. meta, err := json.Marshal(&webhook.TelegramMeta{
  313. BotToken: form.BotToken,
  314. ChatID: form.ChatID,
  315. })
  316. if err != nil {
  317. ctx.ServerError("Marshal", err)
  318. return
  319. }
  320. w := &models.Webhook{
  321. RepoID: orCtx.RepoID,
  322. URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
  323. ContentType: models.ContentTypeJSON,
  324. HookEvent: ParseHookEvent(form.WebhookForm),
  325. IsActive: form.Active,
  326. HookTaskType: models.TELEGRAM,
  327. Meta: string(meta),
  328. OrgID: orCtx.OrgID,
  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. // MSTeamsHooksNewPost response for creating MS Teams hook
  341. func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
  342. ctx.Data["Title"] = ctx.Tr("repo.settings")
  343. ctx.Data["PageIsSettingsHooks"] = true
  344. ctx.Data["PageIsSettingsHooksNew"] = true
  345. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  346. orCtx, err := getOrgRepoCtx(ctx)
  347. if err != nil {
  348. ctx.ServerError("getOrgRepoCtx", err)
  349. return
  350. }
  351. if ctx.HasError() {
  352. ctx.HTML(200, orCtx.NewTemplate)
  353. return
  354. }
  355. w := &models.Webhook{
  356. RepoID: orCtx.RepoID,
  357. URL: form.PayloadURL,
  358. ContentType: models.ContentTypeJSON,
  359. HookEvent: ParseHookEvent(form.WebhookForm),
  360. IsActive: form.Active,
  361. HookTaskType: models.MSTEAMS,
  362. Meta: "",
  363. OrgID: orCtx.OrgID,
  364. }
  365. if err := w.UpdateEvent(); err != nil {
  366. ctx.ServerError("UpdateEvent", err)
  367. return
  368. } else if err := models.CreateWebhook(w); err != nil {
  369. ctx.ServerError("CreateWebhook", err)
  370. return
  371. }
  372. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  373. ctx.Redirect(orCtx.Link)
  374. }
  375. // SlackHooksNewPost response for creating slack hook
  376. func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
  377. ctx.Data["Title"] = ctx.Tr("repo.settings")
  378. ctx.Data["PageIsSettingsHooks"] = true
  379. ctx.Data["PageIsSettingsHooksNew"] = true
  380. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  381. orCtx, err := getOrgRepoCtx(ctx)
  382. if err != nil {
  383. ctx.ServerError("getOrgRepoCtx", err)
  384. return
  385. }
  386. if ctx.HasError() {
  387. ctx.HTML(200, orCtx.NewTemplate)
  388. return
  389. }
  390. if form.HasInvalidChannel() {
  391. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  392. ctx.Redirect(orCtx.Link + "/settings/hooks/slack/new")
  393. return
  394. }
  395. meta, err := json.Marshal(&webhook.SlackMeta{
  396. Channel: strings.TrimSpace(form.Channel),
  397. Username: form.Username,
  398. IconURL: form.IconURL,
  399. Color: form.Color,
  400. })
  401. if err != nil {
  402. ctx.ServerError("Marshal", err)
  403. return
  404. }
  405. w := &models.Webhook{
  406. RepoID: orCtx.RepoID,
  407. URL: form.PayloadURL,
  408. ContentType: models.ContentTypeJSON,
  409. HookEvent: ParseHookEvent(form.WebhookForm),
  410. IsActive: form.Active,
  411. HookTaskType: models.SLACK,
  412. Meta: string(meta),
  413. OrgID: orCtx.OrgID,
  414. }
  415. if err := w.UpdateEvent(); err != nil {
  416. ctx.ServerError("UpdateEvent", err)
  417. return
  418. } else if err := models.CreateWebhook(w); err != nil {
  419. ctx.ServerError("CreateWebhook", err)
  420. return
  421. }
  422. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  423. ctx.Redirect(orCtx.Link)
  424. }
  425. // FeishuHooksNewPost response for creating feishu hook
  426. func FeishuHooksNewPost(ctx *context.Context, form auth.NewFeishuHookForm) {
  427. ctx.Data["Title"] = ctx.Tr("repo.settings")
  428. ctx.Data["PageIsSettingsHooks"] = true
  429. ctx.Data["PageIsSettingsHooksNew"] = true
  430. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  431. orCtx, err := getOrgRepoCtx(ctx)
  432. if err != nil {
  433. ctx.ServerError("getOrgRepoCtx", err)
  434. return
  435. }
  436. if ctx.HasError() {
  437. ctx.HTML(200, orCtx.NewTemplate)
  438. return
  439. }
  440. w := &models.Webhook{
  441. RepoID: orCtx.RepoID,
  442. URL: form.PayloadURL,
  443. ContentType: models.ContentTypeJSON,
  444. HookEvent: ParseHookEvent(form.WebhookForm),
  445. IsActive: form.Active,
  446. HookTaskType: models.FEISHU,
  447. Meta: "",
  448. OrgID: orCtx.OrgID,
  449. }
  450. if err := w.UpdateEvent(); err != nil {
  451. ctx.ServerError("UpdateEvent", err)
  452. return
  453. } else if err := models.CreateWebhook(w); err != nil {
  454. ctx.ServerError("CreateWebhook", err)
  455. return
  456. }
  457. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  458. ctx.Redirect(orCtx.Link)
  459. }
  460. func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
  461. ctx.Data["RequireHighlightJS"] = true
  462. orCtx, err := getOrgRepoCtx(ctx)
  463. if err != nil {
  464. ctx.ServerError("getOrgRepoCtx", err)
  465. return nil, nil
  466. }
  467. ctx.Data["BaseLink"] = orCtx.Link
  468. var w *models.Webhook
  469. if orCtx.RepoID > 0 {
  470. w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
  471. } else if orCtx.OrgID > 0 {
  472. w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
  473. } else {
  474. w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id"))
  475. }
  476. if err != nil {
  477. if models.IsErrWebhookNotExist(err) {
  478. ctx.NotFound("GetWebhookByID", nil)
  479. } else {
  480. ctx.ServerError("GetWebhookByID", err)
  481. }
  482. return nil, nil
  483. }
  484. ctx.Data["HookType"] = w.HookTaskType.Name()
  485. switch w.HookTaskType {
  486. case models.SLACK:
  487. ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
  488. case models.DISCORD:
  489. ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
  490. case models.TELEGRAM:
  491. ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
  492. }
  493. ctx.Data["History"], err = w.History(1)
  494. if err != nil {
  495. ctx.ServerError("History", err)
  496. }
  497. return orCtx, w
  498. }
  499. // WebHooksEdit render editing web hook page
  500. func WebHooksEdit(ctx *context.Context) {
  501. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  502. ctx.Data["PageIsSettingsHooks"] = true
  503. ctx.Data["PageIsSettingsHooksEdit"] = true
  504. orCtx, w := checkWebhook(ctx)
  505. if ctx.Written() {
  506. return
  507. }
  508. ctx.Data["Webhook"] = w
  509. ctx.HTML(200, orCtx.NewTemplate)
  510. }
  511. // WebHooksEditPost response for editing web hook
  512. func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
  513. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  514. ctx.Data["PageIsSettingsHooks"] = true
  515. ctx.Data["PageIsSettingsHooksEdit"] = true
  516. orCtx, w := checkWebhook(ctx)
  517. if ctx.Written() {
  518. return
  519. }
  520. ctx.Data["Webhook"] = w
  521. if ctx.HasError() {
  522. ctx.HTML(200, orCtx.NewTemplate)
  523. return
  524. }
  525. contentType := models.ContentTypeJSON
  526. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  527. contentType = models.ContentTypeForm
  528. }
  529. w.URL = form.PayloadURL
  530. w.ContentType = contentType
  531. w.Secret = form.Secret
  532. w.HookEvent = ParseHookEvent(form.WebhookForm)
  533. w.IsActive = form.Active
  534. w.HTTPMethod = form.HTTPMethod
  535. if err := w.UpdateEvent(); err != nil {
  536. ctx.ServerError("UpdateEvent", err)
  537. return
  538. } else if err := models.UpdateWebhook(w); err != nil {
  539. ctx.ServerError("WebHooksEditPost", err)
  540. return
  541. }
  542. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  543. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  544. }
  545. // GogsHooksEditPost response for editing gogs hook
  546. func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) {
  547. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  548. ctx.Data["PageIsSettingsHooks"] = true
  549. ctx.Data["PageIsSettingsHooksEdit"] = true
  550. orCtx, w := checkWebhook(ctx)
  551. if ctx.Written() {
  552. return
  553. }
  554. ctx.Data["Webhook"] = w
  555. if ctx.HasError() {
  556. ctx.HTML(200, orCtx.NewTemplate)
  557. return
  558. }
  559. contentType := models.ContentTypeJSON
  560. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  561. contentType = models.ContentTypeForm
  562. }
  563. w.URL = form.PayloadURL
  564. w.ContentType = contentType
  565. w.Secret = form.Secret
  566. w.HookEvent = ParseHookEvent(form.WebhookForm)
  567. w.IsActive = form.Active
  568. if err := w.UpdateEvent(); err != nil {
  569. ctx.ServerError("UpdateEvent", err)
  570. return
  571. } else if err := models.UpdateWebhook(w); err != nil {
  572. ctx.ServerError("GogsHooksEditPost", err)
  573. return
  574. }
  575. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  576. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  577. }
  578. // SlackHooksEditPost response for editing slack hook
  579. func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
  580. ctx.Data["Title"] = ctx.Tr("repo.settings")
  581. ctx.Data["PageIsSettingsHooks"] = true
  582. ctx.Data["PageIsSettingsHooksEdit"] = true
  583. orCtx, w := checkWebhook(ctx)
  584. if ctx.Written() {
  585. return
  586. }
  587. ctx.Data["Webhook"] = w
  588. if ctx.HasError() {
  589. ctx.HTML(200, orCtx.NewTemplate)
  590. return
  591. }
  592. if form.HasInvalidChannel() {
  593. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  594. ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
  595. return
  596. }
  597. meta, err := json.Marshal(&webhook.SlackMeta{
  598. Channel: strings.TrimSpace(form.Channel),
  599. Username: form.Username,
  600. IconURL: form.IconURL,
  601. Color: form.Color,
  602. })
  603. if err != nil {
  604. ctx.ServerError("Marshal", err)
  605. return
  606. }
  607. w.URL = form.PayloadURL
  608. w.Meta = string(meta)
  609. w.HookEvent = ParseHookEvent(form.WebhookForm)
  610. w.IsActive = form.Active
  611. if err := w.UpdateEvent(); err != nil {
  612. ctx.ServerError("UpdateEvent", err)
  613. return
  614. } else if err := models.UpdateWebhook(w); err != nil {
  615. ctx.ServerError("UpdateWebhook", err)
  616. return
  617. }
  618. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  619. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  620. }
  621. // DiscordHooksEditPost response for editing discord hook
  622. func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
  623. ctx.Data["Title"] = ctx.Tr("repo.settings")
  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(200, orCtx.NewTemplate)
  633. return
  634. }
  635. meta, err := json.Marshal(&webhook.DiscordMeta{
  636. Username: form.Username,
  637. IconURL: form.IconURL,
  638. })
  639. if err != nil {
  640. ctx.ServerError("Marshal", err)
  641. return
  642. }
  643. w.URL = form.PayloadURL
  644. w.Meta = string(meta)
  645. w.HookEvent = ParseHookEvent(form.WebhookForm)
  646. w.IsActive = form.Active
  647. if err := w.UpdateEvent(); err != nil {
  648. ctx.ServerError("UpdateEvent", err)
  649. return
  650. } else if err := models.UpdateWebhook(w); err != nil {
  651. ctx.ServerError("UpdateWebhook", err)
  652. return
  653. }
  654. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  655. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  656. }
  657. // DingtalkHooksEditPost response for editing discord hook
  658. func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
  659. ctx.Data["Title"] = ctx.Tr("repo.settings")
  660. ctx.Data["PageIsSettingsHooks"] = true
  661. ctx.Data["PageIsSettingsHooksEdit"] = true
  662. orCtx, w := checkWebhook(ctx)
  663. if ctx.Written() {
  664. return
  665. }
  666. ctx.Data["Webhook"] = w
  667. if ctx.HasError() {
  668. ctx.HTML(200, orCtx.NewTemplate)
  669. return
  670. }
  671. w.URL = form.PayloadURL
  672. w.HookEvent = ParseHookEvent(form.WebhookForm)
  673. w.IsActive = form.Active
  674. if err := w.UpdateEvent(); err != nil {
  675. ctx.ServerError("UpdateEvent", err)
  676. return
  677. } else if err := models.UpdateWebhook(w); err != nil {
  678. ctx.ServerError("UpdateWebhook", err)
  679. return
  680. }
  681. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  682. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  683. }
  684. // TelegramHooksEditPost response for editing discord hook
  685. func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) {
  686. ctx.Data["Title"] = ctx.Tr("repo.settings")
  687. ctx.Data["PageIsSettingsHooks"] = true
  688. ctx.Data["PageIsSettingsHooksEdit"] = true
  689. orCtx, w := checkWebhook(ctx)
  690. if ctx.Written() {
  691. return
  692. }
  693. ctx.Data["Webhook"] = w
  694. if ctx.HasError() {
  695. ctx.HTML(200, orCtx.NewTemplate)
  696. return
  697. }
  698. meta, err := json.Marshal(&webhook.TelegramMeta{
  699. BotToken: form.BotToken,
  700. ChatID: form.ChatID,
  701. })
  702. if err != nil {
  703. ctx.ServerError("Marshal", err)
  704. return
  705. }
  706. w.Meta = string(meta)
  707. w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
  708. w.HookEvent = ParseHookEvent(form.WebhookForm)
  709. w.IsActive = form.Active
  710. if err := w.UpdateEvent(); err != nil {
  711. ctx.ServerError("UpdateEvent", err)
  712. return
  713. } else if err := models.UpdateWebhook(w); err != nil {
  714. ctx.ServerError("UpdateWebhook", err)
  715. return
  716. }
  717. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  718. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  719. }
  720. // MSTeamsHooksEditPost response for editing MS Teams hook
  721. func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
  722. ctx.Data["Title"] = ctx.Tr("repo.settings")
  723. ctx.Data["PageIsSettingsHooks"] = true
  724. ctx.Data["PageIsSettingsHooksEdit"] = true
  725. orCtx, w := checkWebhook(ctx)
  726. if ctx.Written() {
  727. return
  728. }
  729. ctx.Data["Webhook"] = w
  730. if ctx.HasError() {
  731. ctx.HTML(200, orCtx.NewTemplate)
  732. return
  733. }
  734. w.URL = form.PayloadURL
  735. w.HookEvent = ParseHookEvent(form.WebhookForm)
  736. w.IsActive = form.Active
  737. if err := w.UpdateEvent(); err != nil {
  738. ctx.ServerError("UpdateEvent", err)
  739. return
  740. } else if err := models.UpdateWebhook(w); err != nil {
  741. ctx.ServerError("UpdateWebhook", err)
  742. return
  743. }
  744. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  745. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  746. }
  747. // FeishuHooksEditPost response for editing feishu hook
  748. func FeishuHooksEditPost(ctx *context.Context, form auth.NewFeishuHookForm) {
  749. ctx.Data["Title"] = ctx.Tr("repo.settings")
  750. ctx.Data["PageIsSettingsHooks"] = true
  751. ctx.Data["PageIsSettingsHooksEdit"] = true
  752. orCtx, w := checkWebhook(ctx)
  753. if ctx.Written() {
  754. return
  755. }
  756. ctx.Data["Webhook"] = w
  757. if ctx.HasError() {
  758. ctx.HTML(200, orCtx.NewTemplate)
  759. return
  760. }
  761. w.URL = form.PayloadURL
  762. w.HookEvent = ParseHookEvent(form.WebhookForm)
  763. w.IsActive = form.Active
  764. if err := w.UpdateEvent(); err != nil {
  765. ctx.ServerError("UpdateEvent", err)
  766. return
  767. } else if err := models.UpdateWebhook(w); err != nil {
  768. ctx.ServerError("UpdateWebhook", err)
  769. return
  770. }
  771. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  772. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  773. }
  774. // TestWebhook test if web hook is work fine
  775. func TestWebhook(ctx *context.Context) {
  776. hookID := ctx.ParamsInt64(":id")
  777. w, err := models.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
  778. if err != nil {
  779. ctx.Flash.Error("GetWebhookByID: " + err.Error())
  780. ctx.Status(500)
  781. return
  782. }
  783. // Grab latest commit or fake one if it's empty repository.
  784. commit := ctx.Repo.Commit
  785. if commit == nil {
  786. ghost := models.NewGhostUser()
  787. commit = &git.Commit{
  788. ID: git.MustIDFromString(git.EmptySHA),
  789. Author: ghost.NewGitSig(),
  790. Committer: ghost.NewGitSig(),
  791. CommitMessage: "This is a fake commit",
  792. }
  793. }
  794. apiUser := ctx.User.APIFormat()
  795. p := &api.PushPayload{
  796. Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
  797. Before: commit.ID.String(),
  798. After: commit.ID.String(),
  799. Commits: []*api.PayloadCommit{
  800. {
  801. ID: commit.ID.String(),
  802. Message: commit.Message(),
  803. URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
  804. Author: &api.PayloadUser{
  805. Name: commit.Author.Name,
  806. Email: commit.Author.Email,
  807. },
  808. Committer: &api.PayloadUser{
  809. Name: commit.Committer.Name,
  810. Email: commit.Committer.Email,
  811. },
  812. },
  813. },
  814. Repo: ctx.Repo.Repository.APIFormat(models.AccessModeNone),
  815. Pusher: apiUser,
  816. Sender: apiUser,
  817. }
  818. if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
  819. ctx.Flash.Error("PrepareWebhook: " + err.Error())
  820. ctx.Status(500)
  821. } else {
  822. ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
  823. ctx.Status(200)
  824. }
  825. }
  826. // DeleteWebhook delete a webhook
  827. func DeleteWebhook(ctx *context.Context) {
  828. if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
  829. ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
  830. } else {
  831. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  832. }
  833. ctx.JSON(200, map[string]interface{}{
  834. "redirect": ctx.Repo.RepoLink + "/settings/hooks",
  835. })
  836. }