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.

278 lines
6.7 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
  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. }
  41. // GetEvent handles conversion from Events to HookEvent.
  42. func (w *Webhook) GetEvent() {
  43. w.HookEvent = &HookEvent{}
  44. if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
  45. log.Error(4, "webhook.GetEvent(%d): %v", w.Id, err)
  46. }
  47. }
  48. func (w *Webhook) GetSlackHook() *Slack {
  49. s := &Slack{}
  50. if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
  51. log.Error(4, "webhook.GetSlackHook(%d): %v", w.Id, err)
  52. }
  53. return s
  54. }
  55. // UpdateEvent handles conversion from HookEvent to Events.
  56. func (w *Webhook) UpdateEvent() error {
  57. data, err := json.Marshal(w.HookEvent)
  58. w.Events = string(data)
  59. return err
  60. }
  61. // HasPushEvent returns true if hook enbaled push event.
  62. func (w *Webhook) HasPushEvent() bool {
  63. if w.PushOnly {
  64. return true
  65. }
  66. return false
  67. }
  68. // CreateWebhook creates a new web hook.
  69. func CreateWebhook(w *Webhook) error {
  70. _, err := x.Insert(w)
  71. return err
  72. }
  73. // GetWebhookById returns webhook by given ID.
  74. func GetWebhookById(hookId int64) (*Webhook, error) {
  75. w := &Webhook{Id: hookId}
  76. has, err := x.Get(w)
  77. if err != nil {
  78. return nil, err
  79. } else if !has {
  80. return nil, ErrWebhookNotExist
  81. }
  82. return w, nil
  83. }
  84. // GetActiveWebhooksByRepoId returns all active webhooks of repository.
  85. func GetActiveWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
  86. err = x.Find(&ws, &Webhook{RepoId: repoId, IsActive: true})
  87. return ws, err
  88. }
  89. // GetWebhooksByRepoId returns all webhooks of repository.
  90. func GetWebhooksByRepoId(repoId int64) (ws []*Webhook, err error) {
  91. err = x.Find(&ws, &Webhook{RepoId: repoId})
  92. return ws, err
  93. }
  94. // UpdateWebhook updates information of webhook.
  95. func UpdateWebhook(w *Webhook) error {
  96. _, err := x.Id(w.Id).AllCols().Update(w)
  97. return err
  98. }
  99. // DeleteWebhook deletes webhook of repository.
  100. func DeleteWebhook(hookId int64) error {
  101. _, err := x.Delete(&Webhook{Id: hookId})
  102. return err
  103. }
  104. // ___ ___ __ ___________ __
  105. // / | \ ____ ____ | | _\__ ___/____ _____| | __
  106. // / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
  107. // \ Y ( <_> | <_> ) < | | / __ \_\___ \| <
  108. // \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
  109. // \/ \/ \/ \/ \/
  110. type HookTaskType int
  111. const (
  112. GOGS HookTaskType = iota + 1
  113. SLACK
  114. )
  115. type HookEventType string
  116. const (
  117. PUSH HookEventType = "push"
  118. )
  119. type PayloadAuthor struct {
  120. Name string `json:"name"`
  121. Email string `json:"email"`
  122. }
  123. type PayloadCommit struct {
  124. Id string `json:"id"`
  125. Message string `json:"message"`
  126. Url string `json:"url"`
  127. Author *PayloadAuthor `json:"author"`
  128. }
  129. type PayloadRepo struct {
  130. Id int64 `json:"id"`
  131. Name string `json:"name"`
  132. Url string `json:"url"`
  133. Description string `json:"description"`
  134. Website string `json:"website"`
  135. Watchers int `json:"watchers"`
  136. Owner *PayloadAuthor `json:"author"`
  137. Private bool `json:"private"`
  138. }
  139. type BasePayload interface {
  140. GetJSONPayload() ([]byte, error)
  141. }
  142. // Payload represents a payload information of hook.
  143. type Payload struct {
  144. Secret string `json:"secret"`
  145. Ref string `json:"ref"`
  146. Commits []*PayloadCommit `json:"commits"`
  147. Repo *PayloadRepo `json:"repository"`
  148. Pusher *PayloadAuthor `json:"pusher"`
  149. }
  150. func (p Payload) GetJSONPayload() ([]byte, error) {
  151. data, err := json.Marshal(p)
  152. if err != nil {
  153. return []byte{}, err
  154. }
  155. return data, nil
  156. }
  157. // HookTask represents a hook task.
  158. type HookTask struct {
  159. Id int64
  160. Uuid string
  161. Type HookTaskType
  162. Url string
  163. BasePayload `xorm:"-"`
  164. PayloadContent string `xorm:"TEXT"`
  165. ContentType HookContentType
  166. EventType HookEventType
  167. IsSsl bool
  168. IsDelivered bool
  169. IsSucceed bool
  170. }
  171. // CreateHookTask creates a new hook task,
  172. // it handles conversion from Payload to PayloadContent.
  173. func CreateHookTask(t *HookTask) error {
  174. data, err := t.BasePayload.GetJSONPayload()
  175. if err != nil {
  176. return err
  177. }
  178. t.Uuid = uuid.NewV4().String()
  179. t.PayloadContent = string(data)
  180. _, err = x.Insert(t)
  181. return err
  182. }
  183. // UpdateHookTask updates information of hook task.
  184. func UpdateHookTask(t *HookTask) error {
  185. _, err := x.AllCols().Update(t)
  186. return err
  187. }
  188. // DeliverHooks checks and delivers undelivered hooks.
  189. func DeliverHooks() {
  190. timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
  191. x.Where("is_delivered=?", false).Iterate(new(HookTask),
  192. func(idx int, bean interface{}) error {
  193. t := bean.(*HookTask)
  194. req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
  195. Header("X-Gogs-Delivery", t.Uuid).
  196. Header("X-Gogs-Event", string(t.EventType))
  197. switch t.ContentType {
  198. case JSON:
  199. req = req.Header("Content-Type", "application/json").Body(t.PayloadContent)
  200. case FORM:
  201. req.Param("payload", t.PayloadContent)
  202. }
  203. t.IsDelivered = true
  204. // TODO: record response.
  205. switch t.Type {
  206. case GOGS:
  207. {
  208. if _, err := req.Response(); err != nil {
  209. log.Error(4, "Delivery: %v", err)
  210. } else {
  211. t.IsSucceed = true
  212. }
  213. }
  214. case SLACK:
  215. {
  216. if res, err := req.Response(); err != nil {
  217. log.Error(4, "Delivery: %v", err)
  218. } else {
  219. defer res.Body.Close()
  220. contents, err := ioutil.ReadAll(res.Body)
  221. if err != nil {
  222. log.Error(4, "%s", err)
  223. } else {
  224. if string(contents) != "ok" {
  225. log.Error(4, "slack failed with: %s", string(contents))
  226. } else {
  227. t.IsSucceed = true
  228. }
  229. }
  230. }
  231. }
  232. }
  233. if err := UpdateHookTask(t); err != nil {
  234. log.Error(4, "UpdateHookTask: %v", err)
  235. return nil
  236. }
  237. log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent)
  238. return nil
  239. })
  240. }