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.

attachment.go 8.7 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2017 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 repo
  5. import (
  6. contexExt "context"
  7. "fmt"
  8. "net/http"
  9. "strconv"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/storage"
  16. "code.gitea.io/gitea/modules/upload"
  17. "code.gitea.io/gitea/modules/worker"
  18. gouuid "github.com/satori/go.uuid"
  19. )
  20. const (
  21. //result of decompress
  22. DecompressSuccess = "0"
  23. DecompressFailed = "1"
  24. )
  25. func RenderAttachmentSettings(ctx *context.Context) {
  26. renderAttachmentSettings(ctx)
  27. }
  28. func renderAttachmentSettings(ctx *context.Context) {
  29. ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
  30. ctx.Data["AttachmentStoreType"] = setting.Attachment.StoreType
  31. ctx.Data["AttachmentAllowedTypes"] = setting.Attachment.AllowedTypes
  32. ctx.Data["AttachmentMaxSize"] = setting.Attachment.MaxSize
  33. ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles
  34. }
  35. // UploadAttachment response for uploading issue's attachment
  36. func UploadAttachment(ctx *context.Context) {
  37. if !setting.Attachment.Enabled {
  38. ctx.Error(404, "attachment is not enabled")
  39. return
  40. }
  41. file, header, err := ctx.Req.FormFile("file")
  42. if err != nil {
  43. ctx.Error(500, fmt.Sprintf("FormFile: %v", err))
  44. return
  45. }
  46. defer file.Close()
  47. buf := make([]byte, 1024)
  48. n, _ := file.Read(buf)
  49. if n > 0 {
  50. buf = buf[:n]
  51. }
  52. err = upload.VerifyAllowedContentType(buf, strings.Split(setting.Attachment.AllowedTypes, ","))
  53. if err != nil {
  54. ctx.Error(400, err.Error())
  55. return
  56. }
  57. datasetID, _ := strconv.ParseInt(ctx.Req.FormValue("dataset_id"), 10, 64)
  58. attach, err := models.NewAttachment(&models.Attachment{
  59. IsPrivate: true,
  60. UploaderID: ctx.User.ID,
  61. Name: header.Filename,
  62. DatasetID: datasetID,
  63. }, buf, file)
  64. if err != nil {
  65. ctx.Error(500, fmt.Sprintf("NewAttachment: %v", err))
  66. return
  67. }
  68. log.Trace("New attachment uploaded: %s", attach.UUID)
  69. ctx.JSON(200, map[string]string{
  70. "uuid": attach.UUID,
  71. })
  72. }
  73. func UpdatePublicAttachment(ctx *context.Context) {
  74. file := ctx.Query("file")
  75. isPrivate, _ := strconv.ParseBool(ctx.Query("is_private"))
  76. attach, err := models.GetAttachmentByUUID(file)
  77. if err != nil {
  78. ctx.Error(404, err.Error())
  79. return
  80. }
  81. attach.IsPrivate = isPrivate
  82. models.UpdateAttachment(attach)
  83. }
  84. // DeleteAttachment response for deleting issue's attachment
  85. func DeleteAttachment(ctx *context.Context) {
  86. file := ctx.Query("file")
  87. attach, err := models.GetAttachmentByUUID(file)
  88. if err != nil {
  89. ctx.Error(400, err.Error())
  90. return
  91. }
  92. if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
  93. ctx.Error(403)
  94. return
  95. }
  96. err = models.DeleteAttachment(attach, true)
  97. if err != nil {
  98. ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
  99. return
  100. }
  101. ctx.JSON(200, map[string]string{
  102. "uuid": attach.UUID,
  103. })
  104. }
  105. // GetAttachment serve attachements
  106. func GetAttachment(ctx *context.Context) {
  107. attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
  108. if err != nil {
  109. if models.IsErrAttachmentNotExist(err) {
  110. ctx.Error(404)
  111. } else {
  112. ctx.ServerError("GetAttachmentByUUID", err)
  113. }
  114. return
  115. }
  116. repository, unitType, err := attach.LinkedRepository()
  117. if err != nil {
  118. ctx.ServerError("LinkedRepository", err)
  119. return
  120. }
  121. if repository == nil { //If not linked
  122. if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) { //We block if not the uploader
  123. ctx.Error(http.StatusNotFound)
  124. return
  125. }
  126. } else { //If we have the repository we check access
  127. perm, err := models.GetUserRepoPermission(repository, ctx.User)
  128. if err != nil {
  129. ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
  130. return
  131. }
  132. if !perm.CanRead(unitType) {
  133. ctx.Error(http.StatusNotFound)
  134. return
  135. }
  136. }
  137. dataSet, err := attach.LinkedDataSet()
  138. if err != nil {
  139. ctx.ServerError("LinkedDataSet", err)
  140. return
  141. }
  142. if dataSet != nil {
  143. isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User)
  144. if err != nil {
  145. ctx.Error(http.StatusInternalServerError, "GetUserDataSetPermission", err.Error())
  146. return
  147. }
  148. if !isPermit {
  149. ctx.Error(http.StatusNotFound)
  150. return
  151. }
  152. }
  153. //If we have matched and access to release or issue
  154. if setting.Attachment.StoreType == storage.MinioStorageType {
  155. url, err := storage.Attachments.PresignedGetURL(attach.RelativePath(), attach.Name)
  156. if err != nil {
  157. ctx.ServerError("PresignedGetURL", err)
  158. return
  159. }
  160. if err = increaseDownloadCount(attach, dataSet); err != nil {
  161. ctx.ServerError("Update", err)
  162. return
  163. }
  164. http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
  165. } else {
  166. fr, err := storage.Attachments.Open(attach.RelativePath())
  167. if err != nil {
  168. ctx.ServerError("Open", err)
  169. return
  170. }
  171. defer fr.Close()
  172. if err = increaseDownloadCount(attach, dataSet); err != nil {
  173. ctx.ServerError("Update", err)
  174. return
  175. }
  176. if err = ServeData(ctx, attach.Name, fr); err != nil {
  177. ctx.ServerError("ServeData", err)
  178. return
  179. }
  180. }
  181. }
  182. func increaseDownloadCount(attach *models.Attachment, dataSet *models.Dataset) error {
  183. if err := attach.IncreaseDownloadCount(); err != nil {
  184. return err
  185. }
  186. if dataSet != nil {
  187. if err := models.IncreaseDownloadCount(dataSet.ID); err != nil {
  188. return err
  189. }
  190. }
  191. return nil
  192. }
  193. // Get a presigned url for put object
  194. func GetPresignedPutObjectURL(ctx *context.Context) {
  195. if !setting.Attachment.Enabled {
  196. ctx.Error(404, "attachment is not enabled")
  197. return
  198. }
  199. err := upload.VerifyFileType(ctx.Params("file_type"), strings.Split(setting.Attachment.AllowedTypes, ","))
  200. if err != nil {
  201. ctx.Error(400, err.Error())
  202. return
  203. }
  204. if setting.Attachment.StoreType == storage.MinioStorageType {
  205. uuid := gouuid.NewV4().String()
  206. url, err := storage.Attachments.PresignedPutURL(models.AttachmentRelativePath(uuid))
  207. if err != nil {
  208. ctx.ServerError("PresignedPutURL", err)
  209. return
  210. }
  211. ctx.JSON(200, map[string]string{
  212. "uuid": uuid,
  213. "url": url,
  214. })
  215. } else {
  216. ctx.Error(404, "storage type is not enabled")
  217. return
  218. }
  219. }
  220. // AddAttachment response for add attachment record
  221. func AddAttachment(ctx *context.Context) {
  222. uuid := ctx.Query("uuid")
  223. has, err := storage.Attachments.HasObject(models.AttachmentRelativePath(uuid))
  224. if err != nil {
  225. ctx.ServerError("HasObject", err)
  226. return
  227. }
  228. if !has {
  229. ctx.Error(404, "attachment has not been uploaded")
  230. return
  231. }
  232. attachment, err := models.InsertAttachment(&models.Attachment{
  233. UUID: uuid,
  234. UploaderID: ctx.User.ID,
  235. IsPrivate: true,
  236. Name: ctx.Query("file_name"),
  237. Size: ctx.QueryInt64("size"),
  238. DatasetID: ctx.QueryInt64("dataset_id"),
  239. })
  240. if err != nil {
  241. ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err))
  242. return
  243. }
  244. if attachment.DatasetID != 0 {
  245. if strings.HasSuffix(attachment.Name, ".zip") {
  246. err = worker.SendDecompressTask(contexExt.Background(), uuid)
  247. if err != nil {
  248. log.Error("SendDecompressTask(%s) failed:%s", uuid, err.Error())
  249. } else {
  250. attachment.DecompressState = models.DecompressStateIng
  251. err = models.UpdateAttachment(attachment)
  252. if err != nil {
  253. log.Error("UpdateAttachment state(%s) failed:%s", uuid, err.Error())
  254. }
  255. }
  256. }
  257. }
  258. ctx.JSON(200, map[string]string{
  259. "result_code": "0",
  260. })
  261. }
  262. func UpdateAttachmentDecompressState(ctx *context.Context) {
  263. uuid := ctx.Query("uuid")
  264. result := ctx.Query("result")
  265. attach, err := models.GetAttachmentByUUID(uuid)
  266. if err != nil {
  267. log.Error("GetAttachmentByUUID(%s) failed:%s", uuid, err.Error())
  268. return
  269. }
  270. if result == DecompressSuccess {
  271. attach.DecompressState = models.DecompressStateDone
  272. } else if result == DecompressFailed {
  273. attach.DecompressState = models.DecompressStateFailed
  274. } else {
  275. log.Error("result is error:", result)
  276. return
  277. }
  278. err = models.UpdateAttachment(attach)
  279. if err != nil {
  280. log.Error("UpdateAttachment(%s) failed:%s", uuid, err.Error())
  281. return
  282. }
  283. ctx.JSON(200, map[string]string{
  284. "result_code": "0",
  285. })
  286. }
  287. func HandleUnDecompressAttachment() {
  288. attachs,err := models.GetUnDecompressAttachments()
  289. if err != nil {
  290. log.Error("GetUnDecompressAttachments failed:", err.Error())
  291. return
  292. }
  293. for _,attach := range attachs {
  294. err = worker.SendDecompressTask(contexExt.Background(), attach.UUID)
  295. if err != nil {
  296. log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error())
  297. } else {
  298. attach.DecompressState = models.DecompressStateIng
  299. err = models.UpdateAttachment(attach)
  300. if err != nil {
  301. log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error())
  302. }
  303. }
  304. }
  305. return
  306. }