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.

302 lines
7.5 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "encoding/json"
  7. "errors"
  8. "io/ioutil"
  9. "time"
  10. "github.com/gogits/gogs/modules/httplib"
  11. "github.com/gogits/gogs/modules/log"
  12. "github.com/gogits/gogs/modules/setting"
  13. "github.com/gogits/gogs/modules/uuid"
  14. )
  15. var (
  16. ErrWebhookNotExist = errors.New("Webhook does not exist")
  17. )
  18. type HookContentType int
  19. const (
  20. JSON HookContentType = iota + 1
  21. FORM
  22. )
  23. // HookEvent represents events that will delivery hook.
  24. type HookEvent struct {
  25. PushOnly bool `json:"push_only"`
  26. }
  27. // Webhook represents a web hook object.
  28. type Webhook struct {
  29. Id int64
  30. RepoId int64
  31. Url string `xorm:"TEXT"`
  32. ContentType HookContentType
  33. Secret string `xorm:"TEXT"`
  34. Events string `xorm:"TEXT"`
  35. *HookEvent `xorm:"-"`
  36. IsSsl bool
  37. IsActive bool
  38. HookTaskType HookTaskType
  39. Meta string `xorm:"TEXT"` // store hook-specific attributes
  40. OrgId int64
  41. }
  42. // GetEvent handles conversion from Events to HookEvent.
  43. func (w *Webhook) GetEvent() {
  44. w.HookEvent = &HookEvent{}
  45. if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
  46. log.Error(4, "webhook.GetEvent(%d): %v", w.Id, err)
  47. }
  48. }
  49. func (w *Webhook) GetSlackHook() *Slack {
  50. s := &Slack{}
  51. if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
  52. log.Error(4, "webhook.GetSlackHook(%d): %v", w.Id, err)
  53. }
  54. return s
  55. }
  56. // UpdateEvent handles conversion from HookEvent to Events.
  57. func (w *Webhook) UpdateEvent() error {
  58. data, err := json.Marshal(w.HookEvent)
  59. w.Events = string(data)
  60. return err
  61. }
  62. // HasPushEvent returns true if hook enbaled push event.
  63. func (w *Webhook) HasPushEvent() bool {
  64. if w.PushOnly {
  65. return true
  66. }
  67. return false
  68. }
  69. // CreateWebhook creates a new web hook.
  70. func CreateWebhook(w *Webhook) error {
  71. _, err := x.Insert(w)
  72. return err
  73. }
  74. // GetWebhookById returns webhook by given ID.
  75. func GetWebhookById(hookId int64) (*Webhook, error) {
  76. w := &Webhook{Id: hookId}
  77. has, err := x.Get(w)
  78. if err != nil {
  79. return nil, err
  80. } else if !has {
  81. return nil, ErrWebhookNotExist
  82. }
  83. return w, nil
  84. }
  85. // GetActiveWebhooksByRepoId returns all active webhooks of repository.
  86. func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
  87. err = x.Where("repo_id=?", repoId).And("is_active=?", true).Find(&ws)
  88. return ws, err
  89. }
  90. // GetWebhooksByRepoId returns all webhooks of repository.
  91. func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
  92. err = x.Find(&ws, &Webhook{RepoId: repoId})
  93. return ws, err
  94. }
  95. // UpdateWebhook updates information of webhook.
  96. func UpdateWebhook(w *Webhook) error {
  97. _, err := x.Id(w.Id).AllCols().Update(w)
  98. return err
  99. }
  100. // DeleteWebhook deletes webhook of repository.
  101. func DeleteWebhook(hookId int64) error {
  102. _, err := x.Delete(&Webhook{Id: hookId})
  103. return err
  104. }
  105. // GetWebhooksByOrgId returns all webhooks for an organization.
  106. func GetWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) {
  107. err = x.Find(&ws, &Webhook{OrgId: orgId})
  108. return ws, err
  109. }
  110. // GetActiveWebhooksByOrgId returns all active webhooks for an organization.
  111. func GetActiveWebhooksByOrgId(orgId int64) (ws []*Webhook, err error) {
  112. err = x.Where("org_id=?", orgId).And("is_active=?", true).Find(&ws)
  113. return ws, err
  114. }
  115. // ___ ___ __ ___________ __
  116. // / | \ ____ ____ | | _\__ ___/____ _____| | __
  117. // / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
  118. // \ Y ( <_> | <_> ) < | | / __ \_\___ \| <
  119. // \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
  120. // \/ \/ \/ \/ \/
  121. type HookTaskType int
  122. const (
  123. GOGS HookTaskType = iota + 1
  124. SLACK
  125. )
  126. type HookEventType string
  127. const (
  128. PUSH HookEventType = "push"
  129. )
  130. type PayloadAuthor struct {
  131. Name string `json:"name"`
  132. Email string `json:"email"`
  133. UserName string `json:"username"`
  134. }
  135. type PayloadCommit struct {
  136. Id string `json:"id"`
  137. Message string `json:"message"`
  138. Url string `json:"url"`
  139. Author *PayloadAuthor `json:"author"`
  140. }
  141. type PayloadRepo struct {
  142. Id int64 `json:"id"`
  143. Name string `json:"name"`
  144. Url string `json:"url"`
  145. Description string `json:"description"`
  146. Website string `json:"website"`
  147. Watchers int `json:"watchers"`
  148. Owner *PayloadAuthor `json:"owner"`
  149. Private bool `json:"private"`
  150. }
  151. type BasePayload interface {
  152. GetJSONPayload() ([]byte, error)
  153. }
  154. // Payload represents a payload information of hook.
  155. type Payload struct {
  156. Secret string `json:"secret"`
  157. Ref string `json:"ref"`
  158. Commits []*PayloadCommit `json:"commits"`
  159. Repo *PayloadRepo `json:"repository"`
  160. Pusher *PayloadAuthor `json:"pusher"`
  161. Before string `json:"before"`
  162. After string `json:"after"`
  163. CompareUrl string `json:"compare_url"`
  164. }
  165. func (p Payload) GetJSONPayload() ([]byte, error) {
  166. data, err := json.Marshal(p)
  167. if err != nil {
  168. return []byte{}, err
  169. }
  170. return data, nil
  171. }
  172. // HookTask represents a hook task.
  173. type HookTask struct {
  174. Id int64
  175. Uuid string
  176. Type HookTaskType
  177. Url string
  178. BasePayload `xorm:"-"`
  179. PayloadContent string `xorm:"TEXT"`
  180. ContentType HookContentType
  181. EventType HookEventType
  182. IsSsl bool
  183. IsDelivered bool
  184. IsSucceed bool
  185. }
  186. // CreateHookTask creates a new hook task,
  187. // it handles conversion from Payload to PayloadContent.
  188. func CreateHookTask(t *HookTask) error {
  189. data, err := t.BasePayload.GetJSONPayload()
  190. if err != nil {
  191. return err
  192. }
  193. t.Uuid = uuid.NewV4().String()
  194. t.PayloadContent = string(data)
  195. _, err = x.Insert(t)
  196. return err
  197. }
  198. // UpdateHookTask updates information of hook task.
  199. func UpdateHookTask(t *HookTask) error {
  200. _, err := x.Id(t.Id).AllCols().Update(t)
  201. return err
  202. }
  203. // DeliverHooks checks and delivers undelivered hooks.
  204. func DeliverHooks() {
  205. tasks := make([]*HookTask, 0, 10)
  206. timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
  207. x.Where("is_delivered=?", false).Iterate(new(HookTask),
  208. func(idx int, bean interface{}) error {
  209. t := bean.(*HookTask)
  210. req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
  211. Header("X-Gogs-Delivery", t.Uuid).
  212. Header("X-Gogs-Event", string(t.EventType))
  213. switch t.ContentType {
  214. case JSON:
  215. req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
  216. case FORM:
  217. req.Param("payload", t.PayloadContent)
  218. }
  219. t.IsDelivered = true
  220. // TODO: record response.
  221. switch t.Type {
  222. case GOGS:
  223. {
  224. if _, err := req.Response(); err != nil {
  225. log.Error(4, "Delivery: %v", err)
  226. } else {
  227. t.IsSucceed = true
  228. }
  229. }
  230. case SLACK:
  231. {
  232. if res, err := req.Response(); err != nil {
  233. log.Error(4, "Delivery: %v", err)
  234. } else {
  235. defer res.Body.Close()
  236. contents, err := ioutil.ReadAll(res.Body)
  237. if err != nil {
  238. log.Error(4, "%s", err)
  239. } else {
  240. if string(contents) != "ok" {
  241. log.Error(4, "slack failed with: %s", string(contents))
  242. } else {
  243. t.IsSucceed = true
  244. }
  245. }
  246. }
  247. }
  248. }
  249. tasks = append(tasks, t)
  250. if t.IsSucceed {
  251. log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent)
  252. }
  253. return nil
  254. })
  255. // Update hook task status.
  256. for _, t := range tasks {
  257. if err := UpdateHookTask(t); err != nil {
  258. log.Error(4, "UpdateHookTask(%d): %v", t.Id, err)
  259. }
  260. }
  261. }