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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  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)
  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. func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
  426. ctx.Data["RequireHighlightJS"] = true
  427. orCtx, err := getOrgRepoCtx(ctx)
  428. if err != nil {
  429. ctx.ServerError("getOrgRepoCtx", err)
  430. return nil, nil
  431. }
  432. ctx.Data["BaseLink"] = orCtx.Link
  433. var w *models.Webhook
  434. if orCtx.RepoID > 0 {
  435. w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
  436. } else if orCtx.OrgID > 0 {
  437. w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
  438. } else {
  439. w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id"))
  440. }
  441. if err != nil {
  442. if models.IsErrWebhookNotExist(err) {
  443. ctx.NotFound("GetWebhookByID", nil)
  444. } else {
  445. ctx.ServerError("GetWebhookByID", err)
  446. }
  447. return nil, nil
  448. }
  449. ctx.Data["HookType"] = w.HookTaskType.Name()
  450. switch w.HookTaskType {
  451. case models.SLACK:
  452. ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
  453. case models.DISCORD:
  454. ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
  455. case models.TELEGRAM:
  456. ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
  457. }
  458. ctx.Data["History"], err = w.History(1)
  459. if err != nil {
  460. ctx.ServerError("History", err)
  461. }
  462. return orCtx, w
  463. }
  464. // WebHooksEdit render editing web hook page
  465. func WebHooksEdit(ctx *context.Context) {
  466. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  467. ctx.Data["PageIsSettingsHooks"] = true
  468. ctx.Data["PageIsSettingsHooksEdit"] = true
  469. orCtx, w := checkWebhook(ctx)
  470. if ctx.Written() {
  471. return
  472. }
  473. ctx.Data["Webhook"] = w
  474. ctx.HTML(200, orCtx.NewTemplate)
  475. }
  476. // WebHooksEditPost response for editing web hook
  477. func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
  478. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  479. ctx.Data["PageIsSettingsHooks"] = true
  480. ctx.Data["PageIsSettingsHooksEdit"] = true
  481. orCtx, w := checkWebhook(ctx)
  482. if ctx.Written() {
  483. return
  484. }
  485. ctx.Data["Webhook"] = w
  486. if ctx.HasError() {
  487. ctx.HTML(200, orCtx.NewTemplate)
  488. return
  489. }
  490. contentType := models.ContentTypeJSON
  491. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  492. contentType = models.ContentTypeForm
  493. }
  494. w.URL = form.PayloadURL
  495. w.ContentType = contentType
  496. w.Secret = form.Secret
  497. w.HookEvent = ParseHookEvent(form.WebhookForm)
  498. w.IsActive = form.Active
  499. w.HTTPMethod = form.HTTPMethod
  500. if err := w.UpdateEvent(); err != nil {
  501. ctx.ServerError("UpdateEvent", err)
  502. return
  503. } else if err := models.UpdateWebhook(w); err != nil {
  504. ctx.ServerError("WebHooksEditPost", err)
  505. return
  506. }
  507. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  508. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  509. }
  510. // GogsHooksEditPost response for editing gogs hook
  511. func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) {
  512. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  513. ctx.Data["PageIsSettingsHooks"] = true
  514. ctx.Data["PageIsSettingsHooksEdit"] = true
  515. orCtx, w := checkWebhook(ctx)
  516. if ctx.Written() {
  517. return
  518. }
  519. ctx.Data["Webhook"] = w
  520. if ctx.HasError() {
  521. ctx.HTML(200, orCtx.NewTemplate)
  522. return
  523. }
  524. contentType := models.ContentTypeJSON
  525. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  526. contentType = models.ContentTypeForm
  527. }
  528. w.URL = form.PayloadURL
  529. w.ContentType = contentType
  530. w.Secret = form.Secret
  531. w.HookEvent = ParseHookEvent(form.WebhookForm)
  532. w.IsActive = form.Active
  533. if err := w.UpdateEvent(); err != nil {
  534. ctx.ServerError("UpdateEvent", err)
  535. return
  536. } else if err := models.UpdateWebhook(w); err != nil {
  537. ctx.ServerError("GogsHooksEditPost", err)
  538. return
  539. }
  540. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  541. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  542. }
  543. // SlackHooksEditPost response for editing slack hook
  544. func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
  545. ctx.Data["Title"] = ctx.Tr("repo.settings")
  546. ctx.Data["PageIsSettingsHooks"] = true
  547. ctx.Data["PageIsSettingsHooksEdit"] = true
  548. orCtx, w := checkWebhook(ctx)
  549. if ctx.Written() {
  550. return
  551. }
  552. ctx.Data["Webhook"] = w
  553. if ctx.HasError() {
  554. ctx.HTML(200, orCtx.NewTemplate)
  555. return
  556. }
  557. if form.HasInvalidChannel() {
  558. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  559. ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
  560. return
  561. }
  562. meta, err := json.Marshal(&webhook.SlackMeta{
  563. Channel: strings.TrimSpace(form.Channel),
  564. Username: form.Username,
  565. IconURL: form.IconURL,
  566. Color: form.Color,
  567. })
  568. if err != nil {
  569. ctx.ServerError("Marshal", err)
  570. return
  571. }
  572. w.URL = form.PayloadURL
  573. w.Meta = string(meta)
  574. w.HookEvent = ParseHookEvent(form.WebhookForm)
  575. w.IsActive = form.Active
  576. if err := w.UpdateEvent(); err != nil {
  577. ctx.ServerError("UpdateEvent", err)
  578. return
  579. } else if err := models.UpdateWebhook(w); err != nil {
  580. ctx.ServerError("UpdateWebhook", err)
  581. return
  582. }
  583. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  584. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  585. }
  586. // DiscordHooksEditPost response for editing discord hook
  587. func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
  588. ctx.Data["Title"] = ctx.Tr("repo.settings")
  589. ctx.Data["PageIsSettingsHooks"] = true
  590. ctx.Data["PageIsSettingsHooksEdit"] = true
  591. orCtx, w := checkWebhook(ctx)
  592. if ctx.Written() {
  593. return
  594. }
  595. ctx.Data["Webhook"] = w
  596. if ctx.HasError() {
  597. ctx.HTML(200, orCtx.NewTemplate)
  598. return
  599. }
  600. meta, err := json.Marshal(&webhook.DiscordMeta{
  601. Username: form.Username,
  602. IconURL: form.IconURL,
  603. })
  604. if err != nil {
  605. ctx.ServerError("Marshal", err)
  606. return
  607. }
  608. w.URL = form.PayloadURL
  609. w.Meta = string(meta)
  610. w.HookEvent = ParseHookEvent(form.WebhookForm)
  611. w.IsActive = form.Active
  612. if err := w.UpdateEvent(); err != nil {
  613. ctx.ServerError("UpdateEvent", err)
  614. return
  615. } else if err := models.UpdateWebhook(w); err != nil {
  616. ctx.ServerError("UpdateWebhook", err)
  617. return
  618. }
  619. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  620. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  621. }
  622. // DingtalkHooksEditPost response for editing discord hook
  623. func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
  624. ctx.Data["Title"] = ctx.Tr("repo.settings")
  625. ctx.Data["PageIsSettingsHooks"] = true
  626. ctx.Data["PageIsSettingsHooksEdit"] = true
  627. orCtx, w := checkWebhook(ctx)
  628. if ctx.Written() {
  629. return
  630. }
  631. ctx.Data["Webhook"] = w
  632. if ctx.HasError() {
  633. ctx.HTML(200, orCtx.NewTemplate)
  634. return
  635. }
  636. w.URL = form.PayloadURL
  637. w.HookEvent = ParseHookEvent(form.WebhookForm)
  638. w.IsActive = form.Active
  639. if err := w.UpdateEvent(); err != nil {
  640. ctx.ServerError("UpdateEvent", err)
  641. return
  642. } else if err := models.UpdateWebhook(w); err != nil {
  643. ctx.ServerError("UpdateWebhook", err)
  644. return
  645. }
  646. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  647. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  648. }
  649. // TelegramHooksEditPost response for editing discord hook
  650. func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) {
  651. ctx.Data["Title"] = ctx.Tr("repo.settings")
  652. ctx.Data["PageIsSettingsHooks"] = true
  653. ctx.Data["PageIsSettingsHooksEdit"] = true
  654. orCtx, w := checkWebhook(ctx)
  655. if ctx.Written() {
  656. return
  657. }
  658. ctx.Data["Webhook"] = w
  659. if ctx.HasError() {
  660. ctx.HTML(200, orCtx.NewTemplate)
  661. return
  662. }
  663. meta, err := json.Marshal(&webhook.TelegramMeta{
  664. BotToken: form.BotToken,
  665. ChatID: form.ChatID,
  666. })
  667. if err != nil {
  668. ctx.ServerError("Marshal", err)
  669. return
  670. }
  671. w.Meta = string(meta)
  672. w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
  673. w.HookEvent = ParseHookEvent(form.WebhookForm)
  674. w.IsActive = form.Active
  675. if err := w.UpdateEvent(); err != nil {
  676. ctx.ServerError("UpdateEvent", err)
  677. return
  678. } else if err := models.UpdateWebhook(w); err != nil {
  679. ctx.ServerError("UpdateWebhook", err)
  680. return
  681. }
  682. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  683. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  684. }
  685. // MSTeamsHooksEditPost response for editing MS Teams hook
  686. func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
  687. ctx.Data["Title"] = ctx.Tr("repo.settings")
  688. ctx.Data["PageIsSettingsHooks"] = true
  689. ctx.Data["PageIsSettingsHooksEdit"] = true
  690. orCtx, w := checkWebhook(ctx)
  691. if ctx.Written() {
  692. return
  693. }
  694. ctx.Data["Webhook"] = w
  695. if ctx.HasError() {
  696. ctx.HTML(200, orCtx.NewTemplate)
  697. return
  698. }
  699. w.URL = form.PayloadURL
  700. w.HookEvent = ParseHookEvent(form.WebhookForm)
  701. w.IsActive = form.Active
  702. if err := w.UpdateEvent(); err != nil {
  703. ctx.ServerError("UpdateEvent", err)
  704. return
  705. } else if err := models.UpdateWebhook(w); err != nil {
  706. ctx.ServerError("UpdateWebhook", err)
  707. return
  708. }
  709. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  710. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  711. }
  712. // TestWebhook test if web hook is work fine
  713. func TestWebhook(ctx *context.Context) {
  714. hookID := ctx.ParamsInt64(":id")
  715. w, err := models.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
  716. if err != nil {
  717. ctx.Flash.Error("GetWebhookByID: " + err.Error())
  718. ctx.Status(500)
  719. return
  720. }
  721. // Grab latest commit or fake one if it's empty repository.
  722. commit := ctx.Repo.Commit
  723. if commit == nil {
  724. ghost := models.NewGhostUser()
  725. commit = &git.Commit{
  726. ID: git.MustIDFromString(git.EmptySHA),
  727. Author: ghost.NewGitSig(),
  728. Committer: ghost.NewGitSig(),
  729. CommitMessage: "This is a fake commit",
  730. }
  731. }
  732. apiUser := ctx.User.APIFormat()
  733. p := &api.PushPayload{
  734. Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
  735. Before: commit.ID.String(),
  736. After: commit.ID.String(),
  737. Commits: []*api.PayloadCommit{
  738. {
  739. ID: commit.ID.String(),
  740. Message: commit.Message(),
  741. URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
  742. Author: &api.PayloadUser{
  743. Name: commit.Author.Name,
  744. Email: commit.Author.Email,
  745. },
  746. Committer: &api.PayloadUser{
  747. Name: commit.Committer.Name,
  748. Email: commit.Committer.Email,
  749. },
  750. },
  751. },
  752. Repo: ctx.Repo.Repository.APIFormat(models.AccessModeNone),
  753. Pusher: apiUser,
  754. Sender: apiUser,
  755. }
  756. if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
  757. ctx.Flash.Error("PrepareWebhook: " + err.Error())
  758. ctx.Status(500)
  759. } else {
  760. ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
  761. ctx.Status(200)
  762. }
  763. }
  764. // DeleteWebhook delete a webhook
  765. func DeleteWebhook(ctx *context.Context) {
  766. if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
  767. ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
  768. } else {
  769. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  770. }
  771. ctx.JSON(200, map[string]interface{}{
  772. "redirect": ctx.Repo.RepoLink + "/settings/hooks",
  773. })
  774. }