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.

1099 lines
30 KiB

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