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 22 kB

4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 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
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "mime/multipart"
  11. "net/http"
  12. "path"
  13. "strconv"
  14. "strings"
  15. "code.gitea.io/gitea/models"
  16. "code.gitea.io/gitea/modules/context"
  17. "code.gitea.io/gitea/modules/log"
  18. "code.gitea.io/gitea/modules/minio_ext"
  19. "code.gitea.io/gitea/modules/setting"
  20. "code.gitea.io/gitea/modules/storage"
  21. "code.gitea.io/gitea/modules/upload"
  22. "code.gitea.io/gitea/modules/worker"
  23. gouuid "github.com/satori/go.uuid"
  24. )
  25. const (
  26. //result of decompress
  27. DecompressSuccess = "0"
  28. DecompressFailed = "1"
  29. )
  30. type CloudBrainDataset struct {
  31. UUID string `json:"id"`
  32. Name string `json:"name"`
  33. Path string `json:"place"`
  34. UserName string `json:"provider"`
  35. CreateTime string `json:"created_at"`
  36. }
  37. type UploadForm struct {
  38. UploadID string `form:"uploadId"`
  39. UuID string `form:"uuid"`
  40. PartSize int64 `form:"size"`
  41. Offset int64 `form:"offset"`
  42. PartNumber int `form:"chunkNumber"`
  43. PartFile multipart.File `form:"file"`
  44. }
  45. func RenderAttachmentSettings(ctx *context.Context) {
  46. renderAttachmentSettings(ctx)
  47. }
  48. func renderAttachmentSettings(ctx *context.Context) {
  49. ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
  50. ctx.Data["AttachmentStoreType"] = setting.Attachment.StoreType
  51. ctx.Data["AttachmentAllowedTypes"] = setting.Attachment.AllowedTypes
  52. ctx.Data["AttachmentMaxSize"] = setting.Attachment.MaxSize
  53. ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles
  54. }
  55. // UploadAttachment response for uploading issue's attachment
  56. func UploadAttachment(ctx *context.Context) {
  57. if !setting.Attachment.Enabled {
  58. ctx.Error(404, "attachment is not enabled")
  59. return
  60. }
  61. file, header, err := ctx.Req.FormFile("file")
  62. if err != nil {
  63. ctx.Error(500, fmt.Sprintf("FormFile: %v", err))
  64. return
  65. }
  66. defer file.Close()
  67. buf := make([]byte, 1024)
  68. n, _ := file.Read(buf)
  69. if n > 0 {
  70. buf = buf[:n]
  71. }
  72. err = upload.VerifyAllowedContentType(buf, strings.Split(setting.Attachment.AllowedTypes, ","))
  73. if err != nil {
  74. ctx.Error(400, err.Error())
  75. return
  76. }
  77. datasetID, _ := strconv.ParseInt(ctx.Req.FormValue("dataset_id"), 10, 64)
  78. attach, err := models.NewAttachment(&models.Attachment{
  79. IsPrivate: true,
  80. UploaderID: ctx.User.ID,
  81. Name: header.Filename,
  82. DatasetID: datasetID,
  83. }, buf, file)
  84. if err != nil {
  85. ctx.Error(500, fmt.Sprintf("NewAttachment: %v", err))
  86. return
  87. }
  88. log.Trace("New attachment uploaded: %s", attach.UUID)
  89. ctx.JSON(200, map[string]string{
  90. "uuid": attach.UUID,
  91. })
  92. }
  93. func UpdatePublicAttachment(ctx *context.Context) {
  94. file := ctx.Query("file")
  95. isPrivate, _ := strconv.ParseBool(ctx.Query("is_private"))
  96. attach, err := models.GetAttachmentByUUID(file)
  97. if err != nil {
  98. ctx.Error(404, err.Error())
  99. return
  100. }
  101. attach.IsPrivate = isPrivate
  102. models.UpdateAttachment(attach)
  103. }
  104. // DeleteAttachment response for deleting issue's attachment
  105. func DeleteAttachment(ctx *context.Context) {
  106. file := ctx.Query("file")
  107. attach, err := models.GetAttachmentByUUID(file)
  108. if err != nil {
  109. ctx.Error(400, err.Error())
  110. return
  111. }
  112. if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
  113. ctx.Error(403)
  114. return
  115. }
  116. err = models.DeleteAttachment(attach, false)
  117. if err != nil {
  118. ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
  119. return
  120. }
  121. ctx.JSON(200, map[string]string{
  122. "uuid": attach.UUID,
  123. })
  124. }
  125. func DownloadUserIsOrgOrCollaboration(ctx *context.Context, attach *models.Attachment) bool {
  126. dataset, err := models.GetDatasetByID(attach.DatasetID)
  127. if err != nil {
  128. log.Info("query dataset error")
  129. } else {
  130. repo, err := models.GetRepositoryByID(dataset.RepoID)
  131. if err != nil {
  132. log.Info("query repo error.")
  133. } else {
  134. repo.GetOwner()
  135. if ctx.User != nil {
  136. if repo.Owner.IsOrganization() {
  137. if repo.Owner.IsUserPartOfOrg(ctx.User.ID) {
  138. log.Info("org user may visit the attach.")
  139. return true
  140. }
  141. }
  142. isCollaborator, _ := repo.IsCollaborator(ctx.User.ID)
  143. if isCollaborator {
  144. log.Info("Collaborator user may visit the attach.")
  145. return true
  146. }
  147. }
  148. }
  149. }
  150. return false
  151. }
  152. // GetAttachment serve attachements
  153. func GetAttachment(ctx *context.Context) {
  154. typeCloudBrain := ctx.QueryInt("type")
  155. err := checkTypeCloudBrain(typeCloudBrain)
  156. if err != nil {
  157. ctx.ServerError("checkTypeCloudBrain failed", err)
  158. return
  159. }
  160. attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
  161. if err != nil {
  162. if models.IsErrAttachmentNotExist(err) {
  163. ctx.Error(404)
  164. } else {
  165. ctx.ServerError("GetAttachmentByUUID", err)
  166. }
  167. return
  168. }
  169. repository, unitType, err := attach.LinkedRepository()
  170. if err != nil {
  171. ctx.ServerError("LinkedRepository", err)
  172. return
  173. }
  174. if repository == nil { //If not linked
  175. //if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader
  176. if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate && !DownloadUserIsOrgOrCollaboration(ctx, attach) { //We block if not the uploader
  177. ctx.Error(http.StatusNotFound)
  178. return
  179. }
  180. } else { //If we have the repository we check access
  181. perm, err := models.GetUserRepoPermission(repository, ctx.User)
  182. if err != nil {
  183. ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
  184. return
  185. }
  186. if !perm.CanRead(unitType) {
  187. ctx.Error(http.StatusNotFound)
  188. return
  189. }
  190. }
  191. dataSet, err := attach.LinkedDataSet()
  192. if err != nil {
  193. ctx.ServerError("LinkedDataSet", err)
  194. return
  195. }
  196. if dataSet != nil {
  197. isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User)
  198. if err != nil {
  199. ctx.Error(http.StatusInternalServerError, "GetUserDataSetPermission", err.Error())
  200. return
  201. }
  202. if !isPermit {
  203. ctx.Error(http.StatusNotFound)
  204. return
  205. }
  206. }
  207. //If we have matched and access to release or issue
  208. if setting.Attachment.StoreType == storage.MinioStorageType {
  209. url := ""
  210. if typeCloudBrain == models.TypeCloudBrainOne {
  211. url, err = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath+attach.RelativePath(), attach.Name)
  212. if err != nil {
  213. ctx.ServerError("PresignedGetURL", err)
  214. return
  215. }
  216. } else {
  217. url, err = storage.ObsGetPreSignedUrl(attach.UUID, attach.Name)
  218. if err != nil {
  219. ctx.ServerError("ObsGetPreSignedUrl", err)
  220. return
  221. }
  222. }
  223. if err = increaseDownloadCount(attach, dataSet); err != nil {
  224. ctx.ServerError("Update", err)
  225. return
  226. }
  227. http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
  228. } else {
  229. fr, err := storage.Attachments.Open(attach.RelativePath())
  230. if err != nil {
  231. ctx.ServerError("Open", err)
  232. return
  233. }
  234. defer fr.Close()
  235. if err = increaseDownloadCount(attach, dataSet); err != nil {
  236. ctx.ServerError("Update", err)
  237. return
  238. }
  239. if err = ServeData(ctx, attach.Name, fr); err != nil {
  240. ctx.ServerError("ServeData", err)
  241. return
  242. }
  243. }
  244. }
  245. func increaseDownloadCount(attach *models.Attachment, dataSet *models.Dataset) error {
  246. if err := attach.IncreaseDownloadCount(); err != nil {
  247. return err
  248. }
  249. if dataSet != nil {
  250. if err := models.IncreaseDownloadCount(dataSet.ID); err != nil {
  251. return err
  252. }
  253. }
  254. return nil
  255. }
  256. // Get a presigned url for put object
  257. func GetPresignedPutObjectURL(ctx *context.Context) {
  258. if !setting.Attachment.Enabled {
  259. ctx.Error(404, "attachment is not enabled")
  260. return
  261. }
  262. err := upload.VerifyFileType(ctx.Params("file_type"), strings.Split(setting.Attachment.AllowedTypes, ","))
  263. if err != nil {
  264. ctx.Error(400, err.Error())
  265. return
  266. }
  267. if setting.Attachment.StoreType == storage.MinioStorageType {
  268. uuid := gouuid.NewV4().String()
  269. url, err := storage.Attachments.PresignedPutURL(models.AttachmentRelativePath(uuid))
  270. if err != nil {
  271. ctx.ServerError("PresignedPutURL", err)
  272. return
  273. }
  274. ctx.JSON(200, map[string]string{
  275. "uuid": uuid,
  276. "url": url,
  277. })
  278. } else {
  279. ctx.Error(404, "storage type is not enabled")
  280. return
  281. }
  282. }
  283. // AddAttachment response for add attachment record
  284. func AddAttachment(ctx *context.Context) {
  285. typeCloudBrain := ctx.QueryInt("type")
  286. err := checkTypeCloudBrain(typeCloudBrain)
  287. if err != nil {
  288. ctx.ServerError("checkTypeCloudBrain failed", err)
  289. return
  290. }
  291. uuid := ctx.Query("uuid")
  292. has := false
  293. if typeCloudBrain == models.TypeCloudBrainOne {
  294. has, err = storage.Attachments.HasObject(models.AttachmentRelativePath(uuid))
  295. if err != nil {
  296. ctx.ServerError("HasObject", err)
  297. return
  298. }
  299. } else {
  300. has, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(uuid) + "/" + uuid)
  301. if err != nil {
  302. ctx.ServerError("ObsHasObject", err)
  303. return
  304. }
  305. }
  306. if !has {
  307. ctx.Error(404, "attachment has not been uploaded")
  308. return
  309. }
  310. attachment, err := models.InsertAttachment(&models.Attachment{
  311. UUID: uuid,
  312. UploaderID: ctx.User.ID,
  313. IsPrivate: true,
  314. Name: ctx.Query("file_name"),
  315. Size: ctx.QueryInt64("size"),
  316. DatasetID: ctx.QueryInt64("dataset_id"),
  317. Type: typeCloudBrain,
  318. })
  319. if err != nil {
  320. ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err))
  321. return
  322. }
  323. if attachment.DatasetID != 0 {
  324. if strings.HasSuffix(attachment.Name, ".zip") {
  325. if typeCloudBrain == models.TypeCloudBrainOne {
  326. err = worker.SendDecompressTask(contexExt.Background(), uuid)
  327. if err != nil {
  328. log.Error("SendDecompressTask(%s) failed:%s", uuid, err.Error())
  329. } else {
  330. attachment.DecompressState = models.DecompressStateIng
  331. err = models.UpdateAttachment(attachment)
  332. if err != nil {
  333. log.Error("UpdateAttachment state(%s) failed:%s", uuid, err.Error())
  334. }
  335. }
  336. }
  337. //todo:decompress type_two
  338. }
  339. }
  340. ctx.JSON(200, map[string]string{
  341. "result_code": "0",
  342. })
  343. }
  344. func UpdateAttachmentDecompressState(ctx *context.Context) {
  345. uuid := ctx.Query("uuid")
  346. result := ctx.Query("result")
  347. attach, err := models.GetAttachmentByUUID(uuid)
  348. if err != nil {
  349. log.Error("GetAttachmentByUUID(%s) failed:%s", uuid, err.Error())
  350. return
  351. }
  352. if result == DecompressSuccess {
  353. attach.DecompressState = models.DecompressStateDone
  354. } else if result == DecompressFailed {
  355. attach.DecompressState = models.DecompressStateFailed
  356. } else {
  357. log.Error("result is error:", result)
  358. return
  359. }
  360. err = models.UpdateAttachment(attach)
  361. if err != nil {
  362. log.Error("UpdateAttachment(%s) failed:%s", uuid, err.Error())
  363. return
  364. }
  365. ctx.JSON(200, map[string]string{
  366. "result_code": "0",
  367. })
  368. }
  369. func GetSuccessChunks(ctx *context.Context) {
  370. fileMD5 := ctx.Query("md5")
  371. typeCloudBrain := ctx.QueryInt("type")
  372. var chunks string
  373. err := checkTypeCloudBrain(typeCloudBrain)
  374. if err != nil {
  375. ctx.ServerError("checkTypeCloudBrain failed", err)
  376. return
  377. }
  378. fileChunk, err := models.GetFileChunkByMD5AndUser(fileMD5, ctx.User.ID, typeCloudBrain)
  379. if err != nil {
  380. if models.IsErrFileChunkNotExist(err) {
  381. ctx.JSON(200, map[string]string{
  382. "uuid": "",
  383. "uploaded": "0",
  384. "uploadID": "",
  385. "chunks": "",
  386. })
  387. } else {
  388. ctx.ServerError("GetFileChunkByMD5", err)
  389. }
  390. return
  391. }
  392. isExist := false
  393. if typeCloudBrain == models.TypeCloudBrainOne {
  394. isExist, err = storage.Attachments.HasObject(models.AttachmentRelativePath(fileChunk.UUID))
  395. if err != nil {
  396. ctx.ServerError("HasObject failed", err)
  397. return
  398. }
  399. } else {
  400. isExist, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(fileChunk.UUID) + "/" + fileChunk.UUID)
  401. if err != nil {
  402. ctx.ServerError("ObsHasObject failed", err)
  403. return
  404. }
  405. }
  406. if isExist {
  407. if fileChunk.IsUploaded == models.FileNotUploaded {
  408. log.Info("the file has been uploaded but not recorded")
  409. fileChunk.IsUploaded = models.FileUploaded
  410. if err = models.UpdateFileChunk(fileChunk); err != nil {
  411. log.Error("UpdateFileChunk failed:", err.Error())
  412. }
  413. }
  414. } else {
  415. if fileChunk.IsUploaded == models.FileUploaded {
  416. log.Info("the file has been recorded but not uploaded")
  417. fileChunk.IsUploaded = models.FileNotUploaded
  418. if err = models.UpdateFileChunk(fileChunk); err != nil {
  419. log.Error("UpdateFileChunk failed:", err.Error())
  420. }
  421. }
  422. if typeCloudBrain == models.TypeCloudBrainOne {
  423. chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID)
  424. if err != nil {
  425. ctx.ServerError("GetPartInfos failed", err)
  426. return
  427. }
  428. } else {
  429. chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID)
  430. if err != nil {
  431. ctx.ServerError("GetObsPartInfos failed", err)
  432. return
  433. }
  434. }
  435. }
  436. var attachID int64
  437. attach, err := models.GetAttachmentByUUID(fileChunk.UUID)
  438. if err != nil {
  439. if models.IsErrAttachmentNotExist(err) {
  440. attachID = 0
  441. } else {
  442. ctx.ServerError("GetAttachmentByUUID", err)
  443. return
  444. }
  445. } else {
  446. attachID = attach.ID
  447. }
  448. if attach == nil {
  449. ctx.JSON(200, map[string]string{
  450. "uuid": fileChunk.UUID,
  451. "uploaded": strconv.Itoa(fileChunk.IsUploaded),
  452. "uploadID": fileChunk.UploadID,
  453. "chunks": string(chunks),
  454. "attachID": "0",
  455. "datasetID": "0",
  456. "fileName": "",
  457. "datasetName": "",
  458. })
  459. return
  460. }
  461. dataset, err := models.GetDatasetByID(attach.DatasetID)
  462. if err != nil {
  463. ctx.ServerError("GetDatasetByID", err)
  464. return
  465. }
  466. ctx.JSON(200, map[string]string{
  467. "uuid": fileChunk.UUID,
  468. "uploaded": strconv.Itoa(fileChunk.IsUploaded),
  469. "uploadID": fileChunk.UploadID,
  470. "chunks": string(chunks),
  471. "attachID": strconv.Itoa(int(attachID)),
  472. "datasetID": strconv.Itoa(int(attach.DatasetID)),
  473. "fileName": attach.Name,
  474. "datasetName": dataset.Title,
  475. })
  476. }
  477. func NewMultipart(ctx *context.Context) {
  478. if !setting.Attachment.Enabled {
  479. ctx.Error(404, "attachment is not enabled")
  480. return
  481. }
  482. err := upload.VerifyFileType(ctx.Query("fileType"), strings.Split(setting.Attachment.AllowedTypes, ","))
  483. if err != nil {
  484. ctx.Error(400, err.Error())
  485. return
  486. }
  487. typeCloudBrain := ctx.QueryInt("type")
  488. err = checkTypeCloudBrain(typeCloudBrain)
  489. if err != nil {
  490. ctx.ServerError("checkTypeCloudBrain failed", err)
  491. return
  492. }
  493. fileName := ctx.Query("file_name")
  494. if setting.Attachment.StoreType == storage.MinioStorageType {
  495. totalChunkCounts := ctx.QueryInt("totalChunkCounts")
  496. if totalChunkCounts > minio_ext.MaxPartsCount {
  497. ctx.Error(400, fmt.Sprintf("chunk counts(%d) is too much", totalChunkCounts))
  498. return
  499. }
  500. fileSize := ctx.QueryInt64("size")
  501. if fileSize > minio_ext.MaxMultipartPutObjectSize {
  502. ctx.Error(400, fmt.Sprintf("file size(%d) is too big", fileSize))
  503. return
  504. }
  505. uuid := gouuid.NewV4().String()
  506. var uploadID string
  507. if typeCloudBrain == models.TypeCloudBrainOne {
  508. uploadID, err = storage.NewMultiPartUpload(uuid)
  509. if err != nil {
  510. ctx.ServerError("NewMultipart", err)
  511. return
  512. }
  513. } else {
  514. uploadID, err = storage.NewObsMultiPartUpload(uuid, fileName)
  515. if err != nil {
  516. ctx.ServerError("NewObsMultiPartUpload", err)
  517. return
  518. }
  519. }
  520. _, err = models.InsertFileChunk(&models.FileChunk{
  521. UUID: uuid,
  522. UserID: ctx.User.ID,
  523. UploadID: uploadID,
  524. Md5: ctx.Query("md5"),
  525. Size: fileSize,
  526. TotalChunks: totalChunkCounts,
  527. Type: typeCloudBrain,
  528. })
  529. if err != nil {
  530. ctx.Error(500, fmt.Sprintf("InsertFileChunk: %v", err))
  531. return
  532. }
  533. ctx.JSON(200, map[string]string{
  534. "uuid": uuid,
  535. "uploadID": uploadID,
  536. })
  537. } else {
  538. ctx.Error(404, "storage type is not enabled")
  539. return
  540. }
  541. }
  542. func GetMultipartUploadUrl(ctx *context.Context) {
  543. uuid := ctx.Query("uuid")
  544. uploadID := ctx.Query("uploadID")
  545. partNumber := ctx.QueryInt("chunkNumber")
  546. size := ctx.QueryInt64("size")
  547. fileName := ctx.Query("file_name")
  548. typeCloudBrain := ctx.QueryInt("type")
  549. err := checkTypeCloudBrain(typeCloudBrain)
  550. if err != nil {
  551. ctx.ServerError("checkTypeCloudBrain failed", err)
  552. return
  553. }
  554. url := ""
  555. if typeCloudBrain == models.TypeCloudBrainOne {
  556. if size > minio_ext.MinPartSize {
  557. ctx.Error(400, fmt.Sprintf("chunk size(%d) is too big", size))
  558. return
  559. }
  560. url, err = storage.GenMultiPartSignedUrl(uuid, uploadID, partNumber, size)
  561. if err != nil {
  562. ctx.Error(500, fmt.Sprintf("GenMultiPartSignedUrl failed: %v", err))
  563. return
  564. }
  565. } else {
  566. url, err = storage.ObsGenMultiPartSignedUrl(uuid, uploadID, partNumber, fileName)
  567. if err != nil {
  568. ctx.Error(500, fmt.Sprintf("ObsGenMultiPartSignedUrl failed: %v", err))
  569. return
  570. }
  571. }
  572. ctx.JSON(200, map[string]string{
  573. "url": url,
  574. })
  575. }
  576. func GetObsKey(ctx *context.Context) {
  577. uuid := gouuid.NewV4().String()
  578. key := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, uuid)), "/")
  579. ctx.JSON(200, map[string]string{
  580. "uuid": uuid,
  581. "key": key,
  582. "access_key_id": setting.AccessKeyID,
  583. "secret_access_key": setting.SecretAccessKey,
  584. "server": setting.Endpoint,
  585. "bucket": setting.Bucket,
  586. })
  587. }
  588. func CompleteMultipart(ctx *context.Context) {
  589. uuid := ctx.Query("uuid")
  590. uploadID := ctx.Query("uploadID")
  591. typeCloudBrain := ctx.QueryInt("type")
  592. fileName := ctx.Query("file_name")
  593. err := checkTypeCloudBrain(typeCloudBrain)
  594. if err != nil {
  595. ctx.ServerError("checkTypeCloudBrain failed", err)
  596. return
  597. }
  598. fileChunk, err := models.GetFileChunkByUUID(uuid)
  599. if err != nil {
  600. if models.IsErrFileChunkNotExist(err) {
  601. ctx.Error(404)
  602. } else {
  603. ctx.ServerError("GetFileChunkByUUID", err)
  604. }
  605. return
  606. }
  607. if typeCloudBrain == models.TypeCloudBrainOne {
  608. _, err = storage.CompleteMultiPartUpload(uuid, uploadID)
  609. if err != nil {
  610. ctx.Error(500, fmt.Sprintf("CompleteMultiPartUpload failed: %v", err))
  611. return
  612. }
  613. } else {
  614. err = storage.CompleteObsMultiPartUpload(uuid, uploadID, fileName)
  615. if err != nil {
  616. ctx.Error(500, fmt.Sprintf("CompleteObsMultiPartUpload failed: %v", err))
  617. return
  618. }
  619. }
  620. fileChunk.IsUploaded = models.FileUploaded
  621. err = models.UpdateFileChunk(fileChunk)
  622. if err != nil {
  623. ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err))
  624. return
  625. }
  626. attachment, err := models.InsertAttachment(&models.Attachment{
  627. UUID: uuid,
  628. UploaderID: ctx.User.ID,
  629. IsPrivate: true,
  630. Name: fileName,
  631. Size: ctx.QueryInt64("size"),
  632. DatasetID: ctx.QueryInt64("dataset_id"),
  633. Type: typeCloudBrain,
  634. })
  635. if err != nil {
  636. ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err))
  637. return
  638. }
  639. if attachment.DatasetID != 0 {
  640. if typeCloudBrain == models.TypeCloudBrainOne {
  641. if strings.HasSuffix(attachment.Name, ".zip") {
  642. err = worker.SendDecompressTask(contexExt.Background(), uuid)
  643. if err != nil {
  644. log.Error("SendDecompressTask(%s) failed:%s", uuid, err.Error())
  645. } else {
  646. attachment.DecompressState = models.DecompressStateIng
  647. err = models.UpdateAttachment(attachment)
  648. if err != nil {
  649. log.Error("UpdateAttachment state(%s) failed:%s", uuid, err.Error())
  650. }
  651. }
  652. }
  653. }
  654. }
  655. ctx.JSON(200, map[string]string{
  656. "result_code": "0",
  657. })
  658. }
  659. func UpdateMultipart(ctx *context.Context) {
  660. uuid := ctx.Query("uuid")
  661. partNumber := ctx.QueryInt("chunkNumber")
  662. etag := ctx.Query("etag")
  663. fileChunk, err := models.GetFileChunkByUUID(uuid)
  664. if err != nil {
  665. if models.IsErrFileChunkNotExist(err) {
  666. ctx.Error(404)
  667. } else {
  668. ctx.ServerError("GetFileChunkByUUID", err)
  669. }
  670. return
  671. }
  672. fileChunk.CompletedParts = append(fileChunk.CompletedParts, strconv.Itoa(partNumber)+"-"+strings.Replace(etag, "\"", "", -1))
  673. err = models.UpdateFileChunk(fileChunk)
  674. if err != nil {
  675. ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err))
  676. return
  677. }
  678. ctx.JSON(200, map[string]string{
  679. "result_code": "0",
  680. })
  681. }
  682. func HandleUnDecompressAttachment() {
  683. attachs, err := models.GetUnDecompressAttachments()
  684. if err != nil {
  685. log.Error("GetUnDecompressAttachments failed:", err.Error())
  686. return
  687. }
  688. for _, attach := range attachs {
  689. err = worker.SendDecompressTask(contexExt.Background(), attach.UUID)
  690. if err != nil {
  691. log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error())
  692. } else {
  693. attach.DecompressState = models.DecompressStateIng
  694. err = models.UpdateAttachment(attach)
  695. if err != nil {
  696. log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error())
  697. }
  698. }
  699. }
  700. return
  701. }
  702. func QueryAllPublicDataset(ctx *context.Context) {
  703. attachs, err := models.GetAllPublicAttachments()
  704. if err != nil {
  705. ctx.JSON(200, map[string]string{
  706. "result_code": "-1",
  707. "error_msg": err.Error(),
  708. "data": "",
  709. })
  710. return
  711. }
  712. queryDatasets(ctx, attachs)
  713. }
  714. func QueryPrivateDataset(ctx *context.Context) {
  715. username := ctx.Params(":username")
  716. attachs, err := models.GetPrivateAttachments(username)
  717. if err != nil {
  718. ctx.JSON(200, map[string]string{
  719. "result_code": "-1",
  720. "error_msg": err.Error(),
  721. "data": "",
  722. })
  723. return
  724. }
  725. for _, attach := range attachs {
  726. attach.Name = username
  727. }
  728. queryDatasets(ctx, attachs)
  729. }
  730. func queryDatasets(ctx *context.Context, attachs []*models.AttachmentUsername) {
  731. var datasets []CloudBrainDataset
  732. if len(attachs) == 0 {
  733. log.Info("dataset is null")
  734. ctx.JSON(200, map[string]string{
  735. "result_code": "0",
  736. "error_msg": "",
  737. "data": "",
  738. })
  739. return
  740. }
  741. for _, attch := range attachs {
  742. has, err := storage.Attachments.HasObject(models.AttachmentRelativePath(attch.UUID))
  743. if err != nil || !has {
  744. continue
  745. }
  746. datasets = append(datasets, CloudBrainDataset{strconv.FormatInt(attch.ID, 10),
  747. attch.Attachment.Name,
  748. setting.Attachment.Minio.RealPath +
  749. setting.Attachment.Minio.Bucket + "/" +
  750. setting.Attachment.Minio.BasePath +
  751. models.AttachmentRelativePath(attch.UUID) +
  752. attch.UUID,
  753. attch.Name,
  754. attch.CreatedUnix.Format("2006-01-02 03:04:05 PM")})
  755. }
  756. data, err := json.Marshal(datasets)
  757. if err != nil {
  758. log.Error("json.Marshal failed:", err.Error())
  759. ctx.JSON(200, map[string]string{
  760. "result_code": "-1",
  761. "error_msg": err.Error(),
  762. "data": "",
  763. })
  764. return
  765. }
  766. ctx.JSON(200, map[string]string{
  767. "result_code": "0",
  768. "error_msg": "",
  769. "data": string(data),
  770. })
  771. return
  772. }
  773. func checkTypeCloudBrain(typeCloudBrain int) error {
  774. if typeCloudBrain != models.TypeCloudBrainOne && typeCloudBrain != models.TypeCloudBrainTwo {
  775. log.Error("type error:", typeCloudBrain)
  776. return errors.New("type error")
  777. }
  778. return nil
  779. }