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

webhook.go 31 kB

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>
5 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  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. HookEvent: ParseHookEvent(form.WebhookForm),
  402. IsActive: form.Active,
  403. HookTaskType: models.MATRIX,
  404. Meta: string(meta),
  405. OrgID: orCtx.OrgID,
  406. IsSystemWebhook: orCtx.IsSystemWebhook,
  407. }
  408. if err := w.UpdateEvent(); err != nil {
  409. ctx.ServerError("UpdateEvent", err)
  410. return
  411. } else if err := models.CreateWebhook(w); err != nil {
  412. ctx.ServerError("CreateWebhook", err)
  413. return
  414. }
  415. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  416. ctx.Redirect(orCtx.Link)
  417. }
  418. // MSTeamsHooksNewPost response for creating MS Teams hook
  419. func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
  420. ctx.Data["Title"] = ctx.Tr("repo.settings")
  421. ctx.Data["PageIsSettingsHooks"] = true
  422. ctx.Data["PageIsSettingsHooksNew"] = true
  423. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  424. ctx.Data["HookType"] = models.MSTEAMS.Name()
  425. orCtx, err := getOrgRepoCtx(ctx)
  426. if err != nil {
  427. ctx.ServerError("getOrgRepoCtx", err)
  428. return
  429. }
  430. if ctx.HasError() {
  431. ctx.HTML(200, orCtx.NewTemplate)
  432. return
  433. }
  434. w := &models.Webhook{
  435. RepoID: orCtx.RepoID,
  436. URL: form.PayloadURL,
  437. ContentType: models.ContentTypeJSON,
  438. HookEvent: ParseHookEvent(form.WebhookForm),
  439. IsActive: form.Active,
  440. HookTaskType: models.MSTEAMS,
  441. Meta: "",
  442. OrgID: orCtx.OrgID,
  443. IsSystemWebhook: orCtx.IsSystemWebhook,
  444. }
  445. if err := w.UpdateEvent(); err != nil {
  446. ctx.ServerError("UpdateEvent", err)
  447. return
  448. } else if err := models.CreateWebhook(w); err != nil {
  449. ctx.ServerError("CreateWebhook", err)
  450. return
  451. }
  452. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  453. ctx.Redirect(orCtx.Link)
  454. }
  455. // SlackHooksNewPost response for creating slack hook
  456. func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
  457. ctx.Data["Title"] = ctx.Tr("repo.settings")
  458. ctx.Data["PageIsSettingsHooks"] = true
  459. ctx.Data["PageIsSettingsHooksNew"] = true
  460. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  461. ctx.Data["HookType"] = models.SLACK.Name()
  462. orCtx, err := getOrgRepoCtx(ctx)
  463. if err != nil {
  464. ctx.ServerError("getOrgRepoCtx", err)
  465. return
  466. }
  467. if ctx.HasError() {
  468. ctx.HTML(200, orCtx.NewTemplate)
  469. return
  470. }
  471. if form.HasInvalidChannel() {
  472. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  473. ctx.Redirect(orCtx.Link + "/slack/new")
  474. return
  475. }
  476. meta, err := json.Marshal(&webhook.SlackMeta{
  477. Channel: strings.TrimSpace(form.Channel),
  478. Username: form.Username,
  479. IconURL: form.IconURL,
  480. Color: form.Color,
  481. })
  482. if err != nil {
  483. ctx.ServerError("Marshal", err)
  484. return
  485. }
  486. w := &models.Webhook{
  487. RepoID: orCtx.RepoID,
  488. URL: form.PayloadURL,
  489. ContentType: models.ContentTypeJSON,
  490. HookEvent: ParseHookEvent(form.WebhookForm),
  491. IsActive: form.Active,
  492. HookTaskType: models.SLACK,
  493. Meta: string(meta),
  494. OrgID: orCtx.OrgID,
  495. IsSystemWebhook: orCtx.IsSystemWebhook,
  496. }
  497. if err := w.UpdateEvent(); err != nil {
  498. ctx.ServerError("UpdateEvent", err)
  499. return
  500. } else if err := models.CreateWebhook(w); err != nil {
  501. ctx.ServerError("CreateWebhook", err)
  502. return
  503. }
  504. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  505. ctx.Redirect(orCtx.Link)
  506. }
  507. // FeishuHooksNewPost response for creating feishu hook
  508. func FeishuHooksNewPost(ctx *context.Context, form auth.NewFeishuHookForm) {
  509. ctx.Data["Title"] = ctx.Tr("repo.settings")
  510. ctx.Data["PageIsSettingsHooks"] = true
  511. ctx.Data["PageIsSettingsHooksNew"] = true
  512. ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
  513. ctx.Data["HookType"] = models.FEISHU.Name()
  514. orCtx, err := getOrgRepoCtx(ctx)
  515. if err != nil {
  516. ctx.ServerError("getOrgRepoCtx", err)
  517. return
  518. }
  519. if ctx.HasError() {
  520. ctx.HTML(200, orCtx.NewTemplate)
  521. return
  522. }
  523. w := &models.Webhook{
  524. RepoID: orCtx.RepoID,
  525. URL: form.PayloadURL,
  526. ContentType: models.ContentTypeJSON,
  527. HookEvent: ParseHookEvent(form.WebhookForm),
  528. IsActive: form.Active,
  529. HookTaskType: models.FEISHU,
  530. Meta: "",
  531. OrgID: orCtx.OrgID,
  532. IsSystemWebhook: orCtx.IsSystemWebhook,
  533. }
  534. if err := w.UpdateEvent(); err != nil {
  535. ctx.ServerError("UpdateEvent", err)
  536. return
  537. } else if err := models.CreateWebhook(w); err != nil {
  538. ctx.ServerError("CreateWebhook", err)
  539. return
  540. }
  541. ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
  542. ctx.Redirect(orCtx.Link)
  543. }
  544. func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
  545. ctx.Data["RequireHighlightJS"] = true
  546. orCtx, err := getOrgRepoCtx(ctx)
  547. if err != nil {
  548. ctx.ServerError("getOrgRepoCtx", err)
  549. return nil, nil
  550. }
  551. ctx.Data["BaseLink"] = orCtx.Link
  552. var w *models.Webhook
  553. if orCtx.RepoID > 0 {
  554. w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
  555. } else if orCtx.OrgID > 0 {
  556. w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
  557. } else if orCtx.IsSystemWebhook {
  558. w, err = models.GetSystemWebhook(ctx.ParamsInt64(":id"))
  559. } else {
  560. w, err = models.GetDefaultWebhook(ctx.ParamsInt64(":id"))
  561. }
  562. if err != nil {
  563. if models.IsErrWebhookNotExist(err) {
  564. ctx.NotFound("GetWebhookByID", nil)
  565. } else {
  566. ctx.ServerError("GetWebhookByID", err)
  567. }
  568. return nil, nil
  569. }
  570. ctx.Data["HookType"] = w.HookTaskType.Name()
  571. switch w.HookTaskType {
  572. case models.SLACK:
  573. ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
  574. case models.DISCORD:
  575. ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
  576. case models.TELEGRAM:
  577. ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
  578. case models.MATRIX:
  579. ctx.Data["MatrixHook"] = webhook.GetMatrixHook(w)
  580. }
  581. ctx.Data["History"], err = w.History(1)
  582. if err != nil {
  583. ctx.ServerError("History", err)
  584. }
  585. return orCtx, w
  586. }
  587. // WebHooksEdit render editing web hook page
  588. func WebHooksEdit(ctx *context.Context) {
  589. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  590. ctx.Data["PageIsSettingsHooks"] = true
  591. ctx.Data["PageIsSettingsHooksEdit"] = true
  592. orCtx, w := checkWebhook(ctx)
  593. if ctx.Written() {
  594. return
  595. }
  596. ctx.Data["Webhook"] = w
  597. ctx.HTML(200, orCtx.NewTemplate)
  598. }
  599. // WebHooksEditPost response for editing web hook
  600. func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
  601. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  602. ctx.Data["PageIsSettingsHooks"] = true
  603. ctx.Data["PageIsSettingsHooksEdit"] = true
  604. orCtx, w := checkWebhook(ctx)
  605. if ctx.Written() {
  606. return
  607. }
  608. ctx.Data["Webhook"] = w
  609. if ctx.HasError() {
  610. ctx.HTML(200, orCtx.NewTemplate)
  611. return
  612. }
  613. contentType := models.ContentTypeJSON
  614. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  615. contentType = models.ContentTypeForm
  616. }
  617. w.URL = form.PayloadURL
  618. w.ContentType = contentType
  619. w.Secret = form.Secret
  620. w.HookEvent = ParseHookEvent(form.WebhookForm)
  621. w.IsActive = form.Active
  622. w.HTTPMethod = form.HTTPMethod
  623. if err := w.UpdateEvent(); err != nil {
  624. ctx.ServerError("UpdateEvent", err)
  625. return
  626. } else if err := models.UpdateWebhook(w); err != nil {
  627. ctx.ServerError("WebHooksEditPost", err)
  628. return
  629. }
  630. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  631. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  632. }
  633. // GogsHooksEditPost response for editing gogs hook
  634. func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) {
  635. ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
  636. ctx.Data["PageIsSettingsHooks"] = true
  637. ctx.Data["PageIsSettingsHooksEdit"] = true
  638. orCtx, w := checkWebhook(ctx)
  639. if ctx.Written() {
  640. return
  641. }
  642. ctx.Data["Webhook"] = w
  643. if ctx.HasError() {
  644. ctx.HTML(200, orCtx.NewTemplate)
  645. return
  646. }
  647. contentType := models.ContentTypeJSON
  648. if models.HookContentType(form.ContentType) == models.ContentTypeForm {
  649. contentType = models.ContentTypeForm
  650. }
  651. w.URL = form.PayloadURL
  652. w.ContentType = contentType
  653. w.Secret = form.Secret
  654. w.HookEvent = ParseHookEvent(form.WebhookForm)
  655. w.IsActive = form.Active
  656. if err := w.UpdateEvent(); err != nil {
  657. ctx.ServerError("UpdateEvent", err)
  658. return
  659. } else if err := models.UpdateWebhook(w); err != nil {
  660. ctx.ServerError("GogsHooksEditPost", err)
  661. return
  662. }
  663. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  664. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  665. }
  666. // SlackHooksEditPost response for editing slack hook
  667. func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
  668. ctx.Data["Title"] = ctx.Tr("repo.settings")
  669. ctx.Data["PageIsSettingsHooks"] = true
  670. ctx.Data["PageIsSettingsHooksEdit"] = true
  671. orCtx, w := checkWebhook(ctx)
  672. if ctx.Written() {
  673. return
  674. }
  675. ctx.Data["Webhook"] = w
  676. if ctx.HasError() {
  677. ctx.HTML(200, orCtx.NewTemplate)
  678. return
  679. }
  680. if form.HasInvalidChannel() {
  681. ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
  682. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  683. return
  684. }
  685. meta, err := json.Marshal(&webhook.SlackMeta{
  686. Channel: strings.TrimSpace(form.Channel),
  687. Username: form.Username,
  688. IconURL: form.IconURL,
  689. Color: form.Color,
  690. })
  691. if err != nil {
  692. ctx.ServerError("Marshal", err)
  693. return
  694. }
  695. w.URL = form.PayloadURL
  696. w.Meta = string(meta)
  697. w.HookEvent = ParseHookEvent(form.WebhookForm)
  698. w.IsActive = form.Active
  699. if err := w.UpdateEvent(); err != nil {
  700. ctx.ServerError("UpdateEvent", err)
  701. return
  702. } else if err := models.UpdateWebhook(w); err != nil {
  703. ctx.ServerError("UpdateWebhook", err)
  704. return
  705. }
  706. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  707. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  708. }
  709. // DiscordHooksEditPost response for editing discord hook
  710. func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
  711. ctx.Data["Title"] = ctx.Tr("repo.settings")
  712. ctx.Data["PageIsSettingsHooks"] = true
  713. ctx.Data["PageIsSettingsHooksEdit"] = true
  714. orCtx, w := checkWebhook(ctx)
  715. if ctx.Written() {
  716. return
  717. }
  718. ctx.Data["Webhook"] = w
  719. if ctx.HasError() {
  720. ctx.HTML(200, orCtx.NewTemplate)
  721. return
  722. }
  723. meta, err := json.Marshal(&webhook.DiscordMeta{
  724. Username: form.Username,
  725. IconURL: form.IconURL,
  726. })
  727. if err != nil {
  728. ctx.ServerError("Marshal", err)
  729. return
  730. }
  731. w.URL = form.PayloadURL
  732. w.Meta = string(meta)
  733. w.HookEvent = ParseHookEvent(form.WebhookForm)
  734. w.IsActive = form.Active
  735. if err := w.UpdateEvent(); err != nil {
  736. ctx.ServerError("UpdateEvent", err)
  737. return
  738. } else if err := models.UpdateWebhook(w); err != nil {
  739. ctx.ServerError("UpdateWebhook", err)
  740. return
  741. }
  742. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  743. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  744. }
  745. // DingtalkHooksEditPost response for editing discord hook
  746. func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
  747. ctx.Data["Title"] = ctx.Tr("repo.settings")
  748. ctx.Data["PageIsSettingsHooks"] = true
  749. ctx.Data["PageIsSettingsHooksEdit"] = true
  750. orCtx, w := checkWebhook(ctx)
  751. if ctx.Written() {
  752. return
  753. }
  754. ctx.Data["Webhook"] = w
  755. if ctx.HasError() {
  756. ctx.HTML(200, orCtx.NewTemplate)
  757. return
  758. }
  759. w.URL = form.PayloadURL
  760. w.HookEvent = ParseHookEvent(form.WebhookForm)
  761. w.IsActive = form.Active
  762. if err := w.UpdateEvent(); err != nil {
  763. ctx.ServerError("UpdateEvent", err)
  764. return
  765. } else if err := models.UpdateWebhook(w); err != nil {
  766. ctx.ServerError("UpdateWebhook", err)
  767. return
  768. }
  769. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  770. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  771. }
  772. // TelegramHooksEditPost response for editing discord hook
  773. func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) {
  774. ctx.Data["Title"] = ctx.Tr("repo.settings")
  775. ctx.Data["PageIsSettingsHooks"] = true
  776. ctx.Data["PageIsSettingsHooksEdit"] = true
  777. orCtx, w := checkWebhook(ctx)
  778. if ctx.Written() {
  779. return
  780. }
  781. ctx.Data["Webhook"] = w
  782. if ctx.HasError() {
  783. ctx.HTML(200, orCtx.NewTemplate)
  784. return
  785. }
  786. meta, err := json.Marshal(&webhook.TelegramMeta{
  787. BotToken: form.BotToken,
  788. ChatID: form.ChatID,
  789. })
  790. if err != nil {
  791. ctx.ServerError("Marshal", err)
  792. return
  793. }
  794. w.Meta = string(meta)
  795. w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
  796. w.HookEvent = ParseHookEvent(form.WebhookForm)
  797. w.IsActive = form.Active
  798. if err := w.UpdateEvent(); err != nil {
  799. ctx.ServerError("UpdateEvent", err)
  800. return
  801. } else if err := models.UpdateWebhook(w); err != nil {
  802. ctx.ServerError("UpdateWebhook", err)
  803. return
  804. }
  805. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  806. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  807. }
  808. // MatrixHooksEditPost response for editing a Matrix hook
  809. func MatrixHooksEditPost(ctx *context.Context, form auth.NewMatrixHookForm) {
  810. ctx.Data["Title"] = ctx.Tr("repo.settings")
  811. ctx.Data["PageIsSettingsHooks"] = true
  812. ctx.Data["PageIsSettingsHooksEdit"] = true
  813. orCtx, w := checkWebhook(ctx)
  814. if ctx.Written() {
  815. return
  816. }
  817. ctx.Data["Webhook"] = w
  818. if ctx.HasError() {
  819. ctx.HTML(200, orCtx.NewTemplate)
  820. return
  821. }
  822. meta, err := json.Marshal(&webhook.MatrixMeta{
  823. HomeserverURL: form.HomeserverURL,
  824. Room: form.RoomID,
  825. AccessToken: form.AccessToken,
  826. MessageType: form.MessageType,
  827. })
  828. if err != nil {
  829. ctx.ServerError("Marshal", err)
  830. return
  831. }
  832. w.Meta = string(meta)
  833. w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID)
  834. w.HookEvent = ParseHookEvent(form.WebhookForm)
  835. w.IsActive = form.Active
  836. if err := w.UpdateEvent(); err != nil {
  837. ctx.ServerError("UpdateEvent", err)
  838. return
  839. } else if err := models.UpdateWebhook(w); err != nil {
  840. ctx.ServerError("UpdateWebhook", err)
  841. return
  842. }
  843. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  844. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  845. }
  846. // MSTeamsHooksEditPost response for editing MS Teams hook
  847. func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
  848. ctx.Data["Title"] = ctx.Tr("repo.settings")
  849. ctx.Data["PageIsSettingsHooks"] = true
  850. ctx.Data["PageIsSettingsHooksEdit"] = true
  851. orCtx, w := checkWebhook(ctx)
  852. if ctx.Written() {
  853. return
  854. }
  855. ctx.Data["Webhook"] = w
  856. if ctx.HasError() {
  857. ctx.HTML(200, orCtx.NewTemplate)
  858. return
  859. }
  860. w.URL = form.PayloadURL
  861. w.HookEvent = ParseHookEvent(form.WebhookForm)
  862. w.IsActive = form.Active
  863. if err := w.UpdateEvent(); err != nil {
  864. ctx.ServerError("UpdateEvent", err)
  865. return
  866. } else if err := models.UpdateWebhook(w); err != nil {
  867. ctx.ServerError("UpdateWebhook", err)
  868. return
  869. }
  870. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  871. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  872. }
  873. // FeishuHooksEditPost response for editing feishu hook
  874. func FeishuHooksEditPost(ctx *context.Context, form auth.NewFeishuHookForm) {
  875. ctx.Data["Title"] = ctx.Tr("repo.settings")
  876. ctx.Data["PageIsSettingsHooks"] = true
  877. ctx.Data["PageIsSettingsHooksEdit"] = true
  878. orCtx, w := checkWebhook(ctx)
  879. if ctx.Written() {
  880. return
  881. }
  882. ctx.Data["Webhook"] = w
  883. if ctx.HasError() {
  884. ctx.HTML(200, orCtx.NewTemplate)
  885. return
  886. }
  887. w.URL = form.PayloadURL
  888. w.HookEvent = ParseHookEvent(form.WebhookForm)
  889. w.IsActive = form.Active
  890. if err := w.UpdateEvent(); err != nil {
  891. ctx.ServerError("UpdateEvent", err)
  892. return
  893. } else if err := models.UpdateWebhook(w); err != nil {
  894. ctx.ServerError("UpdateWebhook", err)
  895. return
  896. }
  897. ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
  898. ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
  899. }
  900. // TestWebhook test if web hook is work fine
  901. func TestWebhook(ctx *context.Context) {
  902. hookID := ctx.ParamsInt64(":id")
  903. w, err := models.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
  904. if err != nil {
  905. ctx.Flash.Error("GetWebhookByID: " + err.Error())
  906. ctx.Status(500)
  907. return
  908. }
  909. // Grab latest commit or fake one if it's empty repository.
  910. commit := ctx.Repo.Commit
  911. if commit == nil {
  912. ghost := models.NewGhostUser()
  913. commit = &git.Commit{
  914. ID: git.MustIDFromString(git.EmptySHA),
  915. Author: ghost.NewGitSig(),
  916. Committer: ghost.NewGitSig(),
  917. CommitMessage: "This is a fake commit",
  918. }
  919. }
  920. apiUser := ctx.User.APIFormat()
  921. p := &api.PushPayload{
  922. Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
  923. Before: commit.ID.String(),
  924. After: commit.ID.String(),
  925. Commits: []*api.PayloadCommit{
  926. {
  927. ID: commit.ID.String(),
  928. Message: commit.Message(),
  929. URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
  930. Author: &api.PayloadUser{
  931. Name: commit.Author.Name,
  932. Email: commit.Author.Email,
  933. },
  934. Committer: &api.PayloadUser{
  935. Name: commit.Committer.Name,
  936. Email: commit.Committer.Email,
  937. },
  938. },
  939. },
  940. Repo: ctx.Repo.Repository.APIFormat(models.AccessModeNone),
  941. Pusher: apiUser,
  942. Sender: apiUser,
  943. }
  944. if err := webhook.PrepareWebhook(w, ctx.Repo.Repository, models.HookEventPush, p); err != nil {
  945. ctx.Flash.Error("PrepareWebhook: " + err.Error())
  946. ctx.Status(500)
  947. } else {
  948. ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
  949. ctx.Status(200)
  950. }
  951. }
  952. // DeleteWebhook delete a webhook
  953. func DeleteWebhook(ctx *context.Context) {
  954. if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
  955. ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
  956. } else {
  957. ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
  958. }
  959. ctx.JSON(200, map[string]interface{}{
  960. "redirect": ctx.Repo.RepoLink + "/settings/hooks",
  961. })
  962. }