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.

227 lines
6.5 KiB

  1. // Copyright 2016 The Gitea 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 utils
  5. import (
  6. api "code.gitea.io/sdk/gitea"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/context"
  9. "code.gitea.io/gitea/routers/api/v1/convert"
  10. "encoding/json"
  11. "github.com/Unknwon/com"
  12. )
  13. // GetOrgHook get an organization's webhook. If there is an error, write to
  14. // `ctx` accordingly and return the error
  15. func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*models.Webhook, error) {
  16. w, err := models.GetWebhookByOrgID(orgID, hookID)
  17. if err != nil {
  18. if models.IsErrWebhookNotExist(err) {
  19. ctx.Status(404)
  20. } else {
  21. ctx.Error(500, "GetWebhookByOrgID", err)
  22. }
  23. return nil, err
  24. }
  25. return w, nil
  26. }
  27. // GetRepoHook get a repo's webhook. If there is an error, write to `ctx`
  28. // accordingly and return the error
  29. func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*models.Webhook, error) {
  30. w, err := models.GetWebhookByRepoID(repoID, hookID)
  31. if err != nil {
  32. if models.IsErrWebhookNotExist(err) {
  33. ctx.Status(404)
  34. } else {
  35. ctx.Error(500, "GetWebhookByID", err)
  36. }
  37. return nil, err
  38. }
  39. return w, nil
  40. }
  41. // CheckCreateHookOption check if a CreateHookOption form is valid. If invalid,
  42. // write the appropriate error to `ctx`. Return whether the form is valid
  43. func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) bool {
  44. if !models.IsValidHookTaskType(form.Type) {
  45. ctx.Error(422, "", "Invalid hook type")
  46. return false
  47. }
  48. for _, name := range []string{"url", "content_type"} {
  49. if _, ok := form.Config[name]; !ok {
  50. ctx.Error(422, "", "Missing config option: "+name)
  51. return false
  52. }
  53. }
  54. if !models.IsValidHookContentType(form.Config["content_type"]) {
  55. ctx.Error(422, "", "Invalid content type")
  56. return false
  57. }
  58. return true
  59. }
  60. // AddOrgHook add a hook to an organization. Writes to `ctx` accordingly
  61. func AddOrgHook(ctx *context.APIContext, form *api.CreateHookOption) {
  62. org := ctx.Org.Organization
  63. hook, ok := addHook(ctx, form, org.ID, 0)
  64. if ok {
  65. ctx.JSON(200, convert.ToHook(org.HomeLink(), hook))
  66. }
  67. }
  68. // AddRepoHook add a hook to a repo. Writes to `ctx` accordingly
  69. func AddRepoHook(ctx *context.APIContext, form *api.CreateHookOption) {
  70. repo := ctx.Repo
  71. hook, ok := addHook(ctx, form, 0, repo.Repository.ID)
  72. if ok {
  73. ctx.JSON(200, convert.ToHook(repo.RepoLink, hook))
  74. }
  75. }
  76. // addHook add the hook specified by `form`, `orgID` and `repoID`. If there is
  77. // an error, write to `ctx` accordingly. Return (webhook, ok)
  78. func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID int64) (*models.Webhook, bool) {
  79. if len(form.Events) == 0 {
  80. form.Events = []string{"push"}
  81. }
  82. w := &models.Webhook{
  83. OrgID: orgID,
  84. RepoID: repoID,
  85. URL: form.Config["url"],
  86. ContentType: models.ToHookContentType(form.Config["content_type"]),
  87. Secret: form.Config["secret"],
  88. HookEvent: &models.HookEvent{
  89. ChooseEvents: true,
  90. HookEvents: models.HookEvents{
  91. Create: com.IsSliceContainsStr(form.Events, string(models.HookEventCreate)),
  92. Push: com.IsSliceContainsStr(form.Events, string(models.HookEventPush)),
  93. PullRequest: com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest)),
  94. },
  95. },
  96. IsActive: form.Active,
  97. HookTaskType: models.ToHookTaskType(form.Type),
  98. }
  99. if w.HookTaskType == models.SLACK {
  100. channel, ok := form.Config["channel"]
  101. if !ok {
  102. ctx.Error(422, "", "Missing config option: channel")
  103. return nil, false
  104. }
  105. meta, err := json.Marshal(&models.SlackMeta{
  106. Channel: channel,
  107. Username: form.Config["username"],
  108. IconURL: form.Config["icon_url"],
  109. Color: form.Config["color"],
  110. })
  111. if err != nil {
  112. ctx.Error(500, "slack: JSON marshal failed", err)
  113. return nil, false
  114. }
  115. w.Meta = string(meta)
  116. }
  117. if err := w.UpdateEvent(); err != nil {
  118. ctx.Error(500, "UpdateEvent", err)
  119. return nil, false
  120. } else if err := models.CreateWebhook(w); err != nil {
  121. ctx.Error(500, "CreateWebhook", err)
  122. return nil, false
  123. }
  124. return w, true
  125. }
  126. // EditOrgHook edit webhook `w` according to `form`. Writes to `ctx` accordingly
  127. func EditOrgHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
  128. org := ctx.Org.Organization
  129. hook, err := GetOrgHook(ctx, org.ID, hookID)
  130. if err != nil {
  131. return
  132. }
  133. if !editHook(ctx, form, hook) {
  134. return
  135. }
  136. updated, err := GetOrgHook(ctx, org.ID, hookID)
  137. if err != nil {
  138. return
  139. }
  140. ctx.JSON(200, convert.ToHook(org.HomeLink(), updated))
  141. }
  142. // EditRepoHook edit webhook `w` according to `form`. Writes to `ctx` accordingly
  143. func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
  144. repo := ctx.Repo
  145. hook, err := GetRepoHook(ctx, repo.Repository.ID, hookID)
  146. if err != nil {
  147. return
  148. }
  149. if !editHook(ctx, form, hook) {
  150. return
  151. }
  152. updated, err := GetRepoHook(ctx, repo.Repository.ID, hookID)
  153. if err != nil {
  154. return
  155. }
  156. ctx.JSON(200, convert.ToHook(repo.RepoLink, updated))
  157. }
  158. // editHook edit the webhook `w` according to `form`. If an error occurs, write
  159. // to `ctx` accordingly and return the error. Return whether successful
  160. func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webhook) bool {
  161. if form.Config != nil {
  162. if url, ok := form.Config["url"]; ok {
  163. w.URL = url
  164. }
  165. if ct, ok := form.Config["content_type"]; ok {
  166. if !models.IsValidHookContentType(ct) {
  167. ctx.Error(422, "", "Invalid content type")
  168. return false
  169. }
  170. w.ContentType = models.ToHookContentType(ct)
  171. }
  172. if w.HookTaskType == models.SLACK {
  173. if channel, ok := form.Config["channel"]; ok {
  174. meta, err := json.Marshal(&models.SlackMeta{
  175. Channel: channel,
  176. Username: form.Config["username"],
  177. IconURL: form.Config["icon_url"],
  178. Color: form.Config["color"],
  179. })
  180. if err != nil {
  181. ctx.Error(500, "slack: JSON marshal failed", err)
  182. return false
  183. }
  184. w.Meta = string(meta)
  185. }
  186. }
  187. }
  188. // Update events
  189. if len(form.Events) == 0 {
  190. form.Events = []string{"push"}
  191. }
  192. w.PushOnly = false
  193. w.SendEverything = false
  194. w.ChooseEvents = true
  195. w.Create = com.IsSliceContainsStr(form.Events, string(models.HookEventCreate))
  196. w.Push = com.IsSliceContainsStr(form.Events, string(models.HookEventPush))
  197. w.PullRequest = com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest))
  198. if err := w.UpdateEvent(); err != nil {
  199. ctx.Error(500, "UpdateEvent", err)
  200. return false
  201. }
  202. if form.Active != nil {
  203. w.IsActive = *form.Active
  204. }
  205. if err := models.UpdateWebhook(w); err != nil {
  206. ctx.Error(500, "UpdateWebhook", err)
  207. return false
  208. }
  209. return true
  210. }