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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. // Copyright 2014 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 models
  6. import (
  7. "encoding/json"
  8. "fmt"
  9. "time"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/setting"
  12. api "code.gitea.io/gitea/modules/structs"
  13. "code.gitea.io/gitea/modules/timeutil"
  14. gouuid "github.com/satori/go.uuid"
  15. )
  16. // HookContentType is the content type of a web hook
  17. type HookContentType int
  18. const (
  19. // ContentTypeJSON is a JSON payload for web hooks
  20. ContentTypeJSON HookContentType = iota + 1
  21. // ContentTypeForm is an url-encoded form payload for web hook
  22. ContentTypeForm
  23. )
  24. var hookContentTypes = map[string]HookContentType{
  25. "json": ContentTypeJSON,
  26. "form": ContentTypeForm,
  27. }
  28. // ToHookContentType returns HookContentType by given name.
  29. func ToHookContentType(name string) HookContentType {
  30. return hookContentTypes[name]
  31. }
  32. // Name returns the name of a given web hook's content type
  33. func (t HookContentType) Name() string {
  34. switch t {
  35. case ContentTypeJSON:
  36. return "json"
  37. case ContentTypeForm:
  38. return "form"
  39. }
  40. return ""
  41. }
  42. // IsValidHookContentType returns true if given name is a valid hook content type.
  43. func IsValidHookContentType(name string) bool {
  44. _, ok := hookContentTypes[name]
  45. return ok
  46. }
  47. // HookEvents is a set of web hook events
  48. type HookEvents struct {
  49. Create bool `json:"create"`
  50. Delete bool `json:"delete"`
  51. Fork bool `json:"fork"`
  52. Issues bool `json:"issues"`
  53. IssueAssign bool `json:"issue_assign"`
  54. IssueLabel bool `json:"issue_label"`
  55. IssueMilestone bool `json:"issue_milestone"`
  56. IssueComment bool `json:"issue_comment"`
  57. Push bool `json:"push"`
  58. PullRequest bool `json:"pull_request"`
  59. PullRequestAssign bool `json:"pull_request_assign"`
  60. PullRequestLabel bool `json:"pull_request_label"`
  61. PullRequestMilestone bool `json:"pull_request_milestone"`
  62. PullRequestComment bool `json:"pull_request_comment"`
  63. PullRequestReview bool `json:"pull_request_review"`
  64. PullRequestSync bool `json:"pull_request_sync"`
  65. Repository bool `json:"repository"`
  66. Release bool `json:"release"`
  67. }
  68. // HookEvent represents events that will delivery hook.
  69. type HookEvent struct {
  70. PushOnly bool `json:"push_only"`
  71. SendEverything bool `json:"send_everything"`
  72. ChooseEvents bool `json:"choose_events"`
  73. BranchFilter string `json:"branch_filter"`
  74. HookEvents `json:"events"`
  75. }
  76. // HookStatus is the status of a web hook
  77. type HookStatus int
  78. // Possible statuses of a web hook
  79. const (
  80. HookStatusNone = iota
  81. HookStatusSucceed
  82. HookStatusFail
  83. )
  84. // Webhook represents a web hook object.
  85. type Webhook struct {
  86. ID int64 `xorm:"pk autoincr"`
  87. RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook
  88. OrgID int64 `xorm:"INDEX"`
  89. IsSystemWebhook bool
  90. URL string `xorm:"url TEXT"`
  91. Signature string `xorm:"TEXT"`
  92. HTTPMethod string `xorm:"http_method"`
  93. ContentType HookContentType
  94. Secret string `xorm:"TEXT"`
  95. Events string `xorm:"TEXT"`
  96. *HookEvent `xorm:"-"`
  97. IsSSL bool `xorm:"is_ssl"`
  98. IsActive bool `xorm:"INDEX"`
  99. HookTaskType HookTaskType
  100. Meta string `xorm:"TEXT"` // store hook-specific attributes
  101. LastStatus HookStatus // Last delivery status
  102. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  103. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  104. }
  105. // AfterLoad updates the webhook object upon setting a column
  106. func (w *Webhook) AfterLoad() {
  107. w.HookEvent = &HookEvent{}
  108. if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
  109. log.Error("Unmarshal[%d]: %v", w.ID, err)
  110. }
  111. }
  112. // History returns history of webhook by given conditions.
  113. func (w *Webhook) History(page int) ([]*HookTask, error) {
  114. return HookTasks(w.ID, page)
  115. }
  116. // UpdateEvent handles conversion from HookEvent to Events.
  117. func (w *Webhook) UpdateEvent() error {
  118. data, err := json.Marshal(w.HookEvent)
  119. w.Events = string(data)
  120. return err
  121. }
  122. // HasCreateEvent returns true if hook enabled create event.
  123. func (w *Webhook) HasCreateEvent() bool {
  124. return w.SendEverything ||
  125. (w.ChooseEvents && w.HookEvents.Create)
  126. }
  127. // HasDeleteEvent returns true if hook enabled delete event.
  128. func (w *Webhook) HasDeleteEvent() bool {
  129. return w.SendEverything ||
  130. (w.ChooseEvents && w.HookEvents.Delete)
  131. }
  132. // HasForkEvent returns true if hook enabled fork event.
  133. func (w *Webhook) HasForkEvent() bool {
  134. return w.SendEverything ||
  135. (w.ChooseEvents && w.HookEvents.Fork)
  136. }
  137. // HasIssuesEvent returns true if hook enabled issues event.
  138. func (w *Webhook) HasIssuesEvent() bool {
  139. return w.SendEverything ||
  140. (w.ChooseEvents && w.HookEvents.Issues)
  141. }
  142. // HasIssuesAssignEvent returns true if hook enabled issues assign event.
  143. func (w *Webhook) HasIssuesAssignEvent() bool {
  144. return w.SendEverything ||
  145. (w.ChooseEvents && w.HookEvents.IssueAssign)
  146. }
  147. // HasIssuesLabelEvent returns true if hook enabled issues label event.
  148. func (w *Webhook) HasIssuesLabelEvent() bool {
  149. return w.SendEverything ||
  150. (w.ChooseEvents && w.HookEvents.IssueLabel)
  151. }
  152. // HasIssuesMilestoneEvent returns true if hook enabled issues milestone event.
  153. func (w *Webhook) HasIssuesMilestoneEvent() bool {
  154. return w.SendEverything ||
  155. (w.ChooseEvents && w.HookEvents.IssueMilestone)
  156. }
  157. // HasIssueCommentEvent returns true if hook enabled issue_comment event.
  158. func (w *Webhook) HasIssueCommentEvent() bool {
  159. return w.SendEverything ||
  160. (w.ChooseEvents && w.HookEvents.IssueComment)
  161. }
  162. // HasPushEvent returns true if hook enabled push event.
  163. func (w *Webhook) HasPushEvent() bool {
  164. return w.PushOnly || w.SendEverything ||
  165. (w.ChooseEvents && w.HookEvents.Push)
  166. }
  167. // HasPullRequestEvent returns true if hook enabled pull request event.
  168. func (w *Webhook) HasPullRequestEvent() bool {
  169. return w.SendEverything ||
  170. (w.ChooseEvents && w.HookEvents.PullRequest)
  171. }
  172. // HasPullRequestAssignEvent returns true if hook enabled pull request assign event.
  173. func (w *Webhook) HasPullRequestAssignEvent() bool {
  174. return w.SendEverything ||
  175. (w.ChooseEvents && w.HookEvents.PullRequestAssign)
  176. }
  177. // HasPullRequestLabelEvent returns true if hook enabled pull request label event.
  178. func (w *Webhook) HasPullRequestLabelEvent() bool {
  179. return w.SendEverything ||
  180. (w.ChooseEvents && w.HookEvents.PullRequestLabel)
  181. }
  182. // HasPullRequestMilestoneEvent returns true if hook enabled pull request milestone event.
  183. func (w *Webhook) HasPullRequestMilestoneEvent() bool {
  184. return w.SendEverything ||
  185. (w.ChooseEvents && w.HookEvents.PullRequestMilestone)
  186. }
  187. // HasPullRequestCommentEvent returns true if hook enabled pull_request_comment event.
  188. func (w *Webhook) HasPullRequestCommentEvent() bool {
  189. return w.SendEverything ||
  190. (w.ChooseEvents && w.HookEvents.PullRequestComment)
  191. }
  192. // HasPullRequestApprovedEvent returns true if hook enabled pull request review event.
  193. func (w *Webhook) HasPullRequestApprovedEvent() bool {
  194. return w.SendEverything ||
  195. (w.ChooseEvents && w.HookEvents.PullRequestReview)
  196. }
  197. // HasPullRequestRejectedEvent returns true if hook enabled pull request review event.
  198. func (w *Webhook) HasPullRequestRejectedEvent() bool {
  199. return w.SendEverything ||
  200. (w.ChooseEvents && w.HookEvents.PullRequestReview)
  201. }
  202. // HasPullRequestReviewCommentEvent returns true if hook enabled pull request review event.
  203. func (w *Webhook) HasPullRequestReviewCommentEvent() bool {
  204. return w.SendEverything ||
  205. (w.ChooseEvents && w.HookEvents.PullRequestReview)
  206. }
  207. // HasPullRequestSyncEvent returns true if hook enabled pull request sync event.
  208. func (w *Webhook) HasPullRequestSyncEvent() bool {
  209. return w.SendEverything ||
  210. (w.ChooseEvents && w.HookEvents.PullRequestSync)
  211. }
  212. // HasReleaseEvent returns if hook enabled release event.
  213. func (w *Webhook) HasReleaseEvent() bool {
  214. return w.SendEverything ||
  215. (w.ChooseEvents && w.HookEvents.Release)
  216. }
  217. // HasRepositoryEvent returns if hook enabled repository event.
  218. func (w *Webhook) HasRepositoryEvent() bool {
  219. return w.SendEverything ||
  220. (w.ChooseEvents && w.HookEvents.Repository)
  221. }
  222. // EventCheckers returns event checkers
  223. func (w *Webhook) EventCheckers() []struct {
  224. Has func() bool
  225. Type HookEventType
  226. } {
  227. return []struct {
  228. Has func() bool
  229. Type HookEventType
  230. }{
  231. {w.HasCreateEvent, HookEventCreate},
  232. {w.HasDeleteEvent, HookEventDelete},
  233. {w.HasForkEvent, HookEventFork},
  234. {w.HasPushEvent, HookEventPush},
  235. {w.HasIssuesEvent, HookEventIssues},
  236. {w.HasIssuesAssignEvent, HookEventIssueAssign},
  237. {w.HasIssuesLabelEvent, HookEventIssueLabel},
  238. {w.HasIssuesMilestoneEvent, HookEventIssueMilestone},
  239. {w.HasIssueCommentEvent, HookEventIssueComment},
  240. {w.HasPullRequestEvent, HookEventPullRequest},
  241. {w.HasPullRequestAssignEvent, HookEventPullRequestAssign},
  242. {w.HasPullRequestLabelEvent, HookEventPullRequestLabel},
  243. {w.HasPullRequestMilestoneEvent, HookEventPullRequestMilestone},
  244. {w.HasPullRequestCommentEvent, HookEventPullRequestComment},
  245. {w.HasPullRequestApprovedEvent, HookEventPullRequestReviewApproved},
  246. {w.HasPullRequestRejectedEvent, HookEventPullRequestReviewRejected},
  247. {w.HasPullRequestCommentEvent, HookEventPullRequestReviewComment},
  248. {w.HasPullRequestSyncEvent, HookEventPullRequestSync},
  249. {w.HasRepositoryEvent, HookEventRepository},
  250. {w.HasReleaseEvent, HookEventRelease},
  251. }
  252. }
  253. // EventsArray returns an array of hook events
  254. func (w *Webhook) EventsArray() []string {
  255. events := make([]string, 0, 7)
  256. for _, c := range w.EventCheckers() {
  257. if c.Has() {
  258. events = append(events, string(c.Type))
  259. }
  260. }
  261. return events
  262. }
  263. // CreateWebhook creates a new web hook.
  264. func CreateWebhook(w *Webhook) error {
  265. return createWebhook(x, w)
  266. }
  267. func createWebhook(e Engine, w *Webhook) error {
  268. _, err := e.Insert(w)
  269. return err
  270. }
  271. // getWebhook uses argument bean as query condition,
  272. // ID must be specified and do not assign unnecessary fields.
  273. func getWebhook(bean *Webhook) (*Webhook, error) {
  274. has, err := x.Get(bean)
  275. if err != nil {
  276. return nil, err
  277. } else if !has {
  278. return nil, ErrWebhookNotExist{bean.ID}
  279. }
  280. return bean, nil
  281. }
  282. // GetWebhookByID returns webhook of repository by given ID.
  283. func GetWebhookByID(id int64) (*Webhook, error) {
  284. return getWebhook(&Webhook{
  285. ID: id,
  286. })
  287. }
  288. // GetWebhookByRepoID returns webhook of repository by given ID.
  289. func GetWebhookByRepoID(repoID, id int64) (*Webhook, error) {
  290. return getWebhook(&Webhook{
  291. ID: id,
  292. RepoID: repoID,
  293. })
  294. }
  295. // GetWebhookByOrgID returns webhook of organization by given ID.
  296. func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
  297. return getWebhook(&Webhook{
  298. ID: id,
  299. OrgID: orgID,
  300. })
  301. }
  302. // GetActiveWebhooksByRepoID returns all active webhooks of repository.
  303. func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
  304. return getActiveWebhooksByRepoID(x, repoID)
  305. }
  306. func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
  307. webhooks := make([]*Webhook, 0, 5)
  308. return webhooks, e.Where("is_active=?", true).
  309. Find(&webhooks, &Webhook{RepoID: repoID})
  310. }
  311. // GetWebhooksByRepoID returns all webhooks of a repository.
  312. func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) {
  313. if listOptions.Page == 0 {
  314. webhooks := make([]*Webhook, 0, 5)
  315. return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
  316. }
  317. sess := listOptions.getPaginatedSession()
  318. webhooks := make([]*Webhook, 0, listOptions.PageSize)
  319. return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID})
  320. }
  321. // GetActiveWebhooksByOrgID returns all active webhooks for an organization.
  322. func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
  323. return getActiveWebhooksByOrgID(x, orgID)
  324. }
  325. func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) {
  326. err = e.
  327. Where("org_id=?", orgID).
  328. And("is_active=?", true).
  329. Find(&ws)
  330. return ws, err
  331. }
  332. // GetWebhooksByOrgID returns paginated webhooks for an organization.
  333. func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error) {
  334. if listOptions.Page == 0 {
  335. ws := make([]*Webhook, 0, 5)
  336. return ws, x.Find(&ws, &Webhook{OrgID: orgID})
  337. }
  338. sess := listOptions.getPaginatedSession()
  339. ws := make([]*Webhook, 0, listOptions.PageSize)
  340. return ws, sess.Find(&ws, &Webhook{OrgID: orgID})
  341. }
  342. // GetDefaultWebhook returns admin-default webhook by given ID.
  343. func GetDefaultWebhook(id int64) (*Webhook, error) {
  344. webhook := &Webhook{ID: id}
  345. has, err := x.
  346. Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false).
  347. Get(webhook)
  348. if err != nil {
  349. return nil, err
  350. } else if !has {
  351. return nil, ErrWebhookNotExist{id}
  352. }
  353. return webhook, nil
  354. }
  355. // GetDefaultWebhooks returns all admin-default webhooks.
  356. func GetDefaultWebhooks() ([]*Webhook, error) {
  357. return getDefaultWebhooks(x)
  358. }
  359. func getDefaultWebhooks(e Engine) ([]*Webhook, error) {
  360. webhooks := make([]*Webhook, 0, 5)
  361. return webhooks, e.
  362. Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false).
  363. Find(&webhooks)
  364. }
  365. // GetSystemWebhook returns admin system webhook by given ID.
  366. func GetSystemWebhook(id int64) (*Webhook, error) {
  367. webhook := &Webhook{ID: id}
  368. has, err := x.
  369. Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
  370. Get(webhook)
  371. if err != nil {
  372. return nil, err
  373. } else if !has {
  374. return nil, ErrWebhookNotExist{id}
  375. }
  376. return webhook, nil
  377. }
  378. // GetSystemWebhooks returns all admin system webhooks.
  379. func GetSystemWebhooks() ([]*Webhook, error) {
  380. return getSystemWebhooks(x)
  381. }
  382. func getSystemWebhooks(e Engine) ([]*Webhook, error) {
  383. webhooks := make([]*Webhook, 0, 5)
  384. return webhooks, e.
  385. Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
  386. Find(&webhooks)
  387. }
  388. // UpdateWebhook updates information of webhook.
  389. func UpdateWebhook(w *Webhook) error {
  390. _, err := x.ID(w.ID).AllCols().Update(w)
  391. return err
  392. }
  393. // UpdateWebhookLastStatus updates last status of webhook.
  394. func UpdateWebhookLastStatus(w *Webhook) error {
  395. _, err := x.ID(w.ID).Cols("last_status").Update(w)
  396. return err
  397. }
  398. // deleteWebhook uses argument bean as query condition,
  399. // ID must be specified and do not assign unnecessary fields.
  400. func deleteWebhook(bean *Webhook) (err error) {
  401. sess := x.NewSession()
  402. defer sess.Close()
  403. if err = sess.Begin(); err != nil {
  404. return err
  405. }
  406. if count, err := sess.Delete(bean); err != nil {
  407. return err
  408. } else if count == 0 {
  409. return ErrWebhookNotExist{ID: bean.ID}
  410. } else if _, err = sess.Delete(&HookTask{HookID: bean.ID}); err != nil {
  411. return err
  412. }
  413. return sess.Commit()
  414. }
  415. // DeleteWebhookByRepoID deletes webhook of repository by given ID.
  416. func DeleteWebhookByRepoID(repoID, id int64) error {
  417. return deleteWebhook(&Webhook{
  418. ID: id,
  419. RepoID: repoID,
  420. })
  421. }
  422. // DeleteWebhookByOrgID deletes webhook of organization by given ID.
  423. func DeleteWebhookByOrgID(orgID, id int64) error {
  424. return deleteWebhook(&Webhook{
  425. ID: id,
  426. OrgID: orgID,
  427. })
  428. }
  429. // DeleteDefaultSystemWebhook deletes an admin-configured default or system webhook (where Org and Repo ID both 0)
  430. func DeleteDefaultSystemWebhook(id int64) error {
  431. sess := x.NewSession()
  432. defer sess.Close()
  433. if err := sess.Begin(); err != nil {
  434. return err
  435. }
  436. count, err := sess.
  437. Where("repo_id=? AND org_id=?", 0, 0).
  438. Delete(&Webhook{ID: id})
  439. if err != nil {
  440. return err
  441. } else if count == 0 {
  442. return ErrWebhookNotExist{ID: id}
  443. }
  444. if _, err := sess.Delete(&HookTask{HookID: id}); err != nil {
  445. return err
  446. }
  447. return sess.Commit()
  448. }
  449. // copyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
  450. func copyDefaultWebhooksToRepo(e Engine, repoID int64) error {
  451. ws, err := getDefaultWebhooks(e)
  452. if err != nil {
  453. return fmt.Errorf("GetDefaultWebhooks: %v", err)
  454. }
  455. for _, w := range ws {
  456. w.ID = 0
  457. w.RepoID = repoID
  458. if err := createWebhook(e, w); err != nil {
  459. return fmt.Errorf("CreateWebhook: %v", err)
  460. }
  461. }
  462. return nil
  463. }
  464. // ___ ___ __ ___________ __
  465. // / | \ ____ ____ | | _\__ ___/____ _____| | __
  466. // / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
  467. // \ Y ( <_> | <_> ) < | | / __ \_\___ \| <
  468. // \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
  469. // \/ \/ \/ \/ \/
  470. // HookTaskType is the type of an hook task
  471. type HookTaskType int
  472. // Types of hook tasks
  473. const (
  474. GOGS HookTaskType = iota + 1
  475. SLACK
  476. GITEA
  477. DISCORD
  478. DINGTALK
  479. TELEGRAM
  480. MSTEAMS
  481. FEISHU
  482. MATRIX
  483. )
  484. var hookTaskTypes = map[string]HookTaskType{
  485. "gitea": GITEA,
  486. "gogs": GOGS,
  487. "slack": SLACK,
  488. "discord": DISCORD,
  489. "dingtalk": DINGTALK,
  490. "telegram": TELEGRAM,
  491. "msteams": MSTEAMS,
  492. "feishu": FEISHU,
  493. "matrix": MATRIX,
  494. }
  495. // ToHookTaskType returns HookTaskType by given name.
  496. func ToHookTaskType(name string) HookTaskType {
  497. return hookTaskTypes[name]
  498. }
  499. // Name returns the name of an hook task type
  500. func (t HookTaskType) Name() string {
  501. switch t {
  502. case GITEA:
  503. return "gitea"
  504. case GOGS:
  505. return "gogs"
  506. case SLACK:
  507. return "slack"
  508. case DISCORD:
  509. return "discord"
  510. case DINGTALK:
  511. return "dingtalk"
  512. case TELEGRAM:
  513. return "telegram"
  514. case MSTEAMS:
  515. return "msteams"
  516. case FEISHU:
  517. return "feishu"
  518. case MATRIX:
  519. return "matrix"
  520. }
  521. return ""
  522. }
  523. // IsValidHookTaskType returns true if given name is a valid hook task type.
  524. func IsValidHookTaskType(name string) bool {
  525. _, ok := hookTaskTypes[name]
  526. return ok
  527. }
  528. // HookEventType is the type of an hook event
  529. type HookEventType string
  530. // Types of hook events
  531. const (
  532. HookEventCreate HookEventType = "create"
  533. HookEventDelete HookEventType = "delete"
  534. HookEventFork HookEventType = "fork"
  535. HookEventPush HookEventType = "push"
  536. HookEventIssues HookEventType = "issues"
  537. HookEventIssueAssign HookEventType = "issue_assign"
  538. HookEventIssueLabel HookEventType = "issue_label"
  539. HookEventIssueMilestone HookEventType = "issue_milestone"
  540. HookEventIssueComment HookEventType = "issue_comment"
  541. HookEventPullRequest HookEventType = "pull_request"
  542. HookEventPullRequestAssign HookEventType = "pull_request_assign"
  543. HookEventPullRequestLabel HookEventType = "pull_request_label"
  544. HookEventPullRequestMilestone HookEventType = "pull_request_milestone"
  545. HookEventPullRequestComment HookEventType = "pull_request_comment"
  546. HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved"
  547. HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
  548. HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
  549. HookEventPullRequestSync HookEventType = "pull_request_sync"
  550. HookEventRepository HookEventType = "repository"
  551. HookEventRelease HookEventType = "release"
  552. )
  553. // Event returns the HookEventType as an event string
  554. func (h HookEventType) Event() string {
  555. switch h {
  556. case HookEventCreate:
  557. return "create"
  558. case HookEventDelete:
  559. return "delete"
  560. case HookEventFork:
  561. return "fork"
  562. case HookEventPush:
  563. return "push"
  564. case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
  565. return "issues"
  566. case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
  567. HookEventPullRequestSync:
  568. return "pull_request"
  569. case HookEventIssueComment, HookEventPullRequestComment:
  570. return "issue_comment"
  571. case HookEventPullRequestReviewApproved:
  572. return "pull_request_approved"
  573. case HookEventPullRequestReviewRejected:
  574. return "pull_request_rejected"
  575. case HookEventPullRequestReviewComment:
  576. return "pull_request_comment"
  577. case HookEventRepository:
  578. return "repository"
  579. case HookEventRelease:
  580. return "release"
  581. }
  582. return ""
  583. }
  584. // HookRequest represents hook task request information.
  585. type HookRequest struct {
  586. Headers map[string]string `json:"headers"`
  587. }
  588. // HookResponse represents hook task response information.
  589. type HookResponse struct {
  590. Status int `json:"status"`
  591. Headers map[string]string `json:"headers"`
  592. Body string `json:"body"`
  593. }
  594. // HookTask represents a hook task.
  595. type HookTask struct {
  596. ID int64 `xorm:"pk autoincr"`
  597. RepoID int64 `xorm:"INDEX"`
  598. HookID int64
  599. UUID string
  600. Type HookTaskType
  601. URL string `xorm:"TEXT"`
  602. Signature string `xorm:"TEXT"`
  603. api.Payloader `xorm:"-"`
  604. PayloadContent string `xorm:"TEXT"`
  605. HTTPMethod string `xorm:"http_method"`
  606. ContentType HookContentType
  607. EventType HookEventType
  608. IsSSL bool
  609. IsDelivered bool
  610. Delivered int64
  611. DeliveredString string `xorm:"-"`
  612. // History info.
  613. IsSucceed bool
  614. RequestContent string `xorm:"TEXT"`
  615. RequestInfo *HookRequest `xorm:"-"`
  616. ResponseContent string `xorm:"TEXT"`
  617. ResponseInfo *HookResponse `xorm:"-"`
  618. }
  619. // BeforeUpdate will be invoked by XORM before updating a record
  620. // representing this object
  621. func (t *HookTask) BeforeUpdate() {
  622. if t.RequestInfo != nil {
  623. t.RequestContent = t.simpleMarshalJSON(t.RequestInfo)
  624. }
  625. if t.ResponseInfo != nil {
  626. t.ResponseContent = t.simpleMarshalJSON(t.ResponseInfo)
  627. }
  628. }
  629. // AfterLoad updates the webhook object upon setting a column
  630. func (t *HookTask) AfterLoad() {
  631. t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
  632. if len(t.RequestContent) == 0 {
  633. return
  634. }
  635. t.RequestInfo = &HookRequest{}
  636. if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
  637. log.Error("Unmarshal RequestContent[%d]: %v", t.ID, err)
  638. }
  639. if len(t.ResponseContent) > 0 {
  640. t.ResponseInfo = &HookResponse{}
  641. if err := json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
  642. log.Error("Unmarshal ResponseContent[%d]: %v", t.ID, err)
  643. }
  644. }
  645. }
  646. func (t *HookTask) simpleMarshalJSON(v interface{}) string {
  647. p, err := json.Marshal(v)
  648. if err != nil {
  649. log.Error("Marshal [%d]: %v", t.ID, err)
  650. }
  651. return string(p)
  652. }
  653. // HookTasks returns a list of hook tasks by given conditions.
  654. func HookTasks(hookID int64, page int) ([]*HookTask, error) {
  655. tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
  656. return tasks, x.
  657. Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).
  658. Where("hook_id=?", hookID).
  659. Desc("id").
  660. Find(&tasks)
  661. }
  662. // CreateHookTask creates a new hook task,
  663. // it handles conversion from Payload to PayloadContent.
  664. func CreateHookTask(t *HookTask) error {
  665. return createHookTask(x, t)
  666. }
  667. func createHookTask(e Engine, t *HookTask) error {
  668. data, err := t.Payloader.JSONPayload()
  669. if err != nil {
  670. return err
  671. }
  672. t.UUID = gouuid.NewV4().String()
  673. t.PayloadContent = string(data)
  674. _, err = e.Insert(t)
  675. return err
  676. }
  677. // UpdateHookTask updates information of hook task.
  678. func UpdateHookTask(t *HookTask) error {
  679. _, err := x.ID(t.ID).AllCols().Update(t)
  680. return err
  681. }
  682. // FindUndeliveredHookTasks represents find the undelivered hook tasks
  683. func FindUndeliveredHookTasks() ([]*HookTask, error) {
  684. tasks := make([]*HookTask, 0, 10)
  685. if err := x.Where("is_delivered=?", false).Find(&tasks); err != nil {
  686. return nil, err
  687. }
  688. return tasks, nil
  689. }
  690. // FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
  691. func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
  692. tasks := make([]*HookTask, 0, 5)
  693. if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
  694. return nil, err
  695. }
  696. return tasks, nil
  697. }