gitea源码

cran.go 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cran
  4. import (
  5. "compress/gzip"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "strings"
  11. packages_model "code.gitea.io/gitea/models/packages"
  12. cran_model "code.gitea.io/gitea/models/packages/cran"
  13. packages_module "code.gitea.io/gitea/modules/packages"
  14. cran_module "code.gitea.io/gitea/modules/packages/cran"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/routers/api/packages/helper"
  17. "code.gitea.io/gitea/services/context"
  18. packages_service "code.gitea.io/gitea/services/packages"
  19. )
  20. func apiError(ctx *context.Context, status int, obj any) {
  21. message := helper.ProcessErrorForUser(ctx, status, obj)
  22. ctx.PlainText(status, message)
  23. }
  24. func EnumerateSourcePackages(ctx *context.Context) {
  25. enumeratePackages(ctx, ctx.PathParam("format"), &cran_model.SearchOptions{
  26. OwnerID: ctx.Package.Owner.ID,
  27. FileType: cran_module.TypeSource,
  28. })
  29. }
  30. func EnumerateBinaryPackages(ctx *context.Context) {
  31. enumeratePackages(ctx, ctx.PathParam("format"), &cran_model.SearchOptions{
  32. OwnerID: ctx.Package.Owner.ID,
  33. FileType: cran_module.TypeBinary,
  34. Platform: ctx.PathParam("platform"),
  35. RVersion: ctx.PathParam("rversion"),
  36. })
  37. }
  38. func enumeratePackages(ctx *context.Context, format string, opts *cran_model.SearchOptions) {
  39. if format != "" && format != ".gz" {
  40. apiError(ctx, http.StatusNotFound, nil)
  41. return
  42. }
  43. pvs, err := cran_model.SearchLatestVersions(ctx, opts)
  44. if err != nil {
  45. apiError(ctx, http.StatusInternalServerError, err)
  46. return
  47. }
  48. if len(pvs) == 0 {
  49. apiError(ctx, http.StatusNotFound, nil)
  50. return
  51. }
  52. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  53. if err != nil {
  54. apiError(ctx, http.StatusInternalServerError, err)
  55. return
  56. }
  57. var w io.Writer = ctx.Resp
  58. if format == ".gz" {
  59. ctx.Resp.Header().Set("Content-Type", "application/x-gzip")
  60. gzw := gzip.NewWriter(w)
  61. defer gzw.Close()
  62. w = gzw
  63. } else {
  64. ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
  65. }
  66. ctx.Resp.WriteHeader(http.StatusOK)
  67. for i, pd := range pds {
  68. if i > 0 {
  69. fmt.Fprintln(w)
  70. }
  71. var pfd *packages_model.PackageFileDescriptor
  72. for _, d := range pd.Files {
  73. if d.Properties.GetByName(cran_module.PropertyType) == opts.FileType &&
  74. d.Properties.GetByName(cran_module.PropertyPlatform) == opts.Platform &&
  75. d.Properties.GetByName(cran_module.PropertyRVersion) == opts.RVersion {
  76. pfd = d
  77. break
  78. }
  79. }
  80. metadata := pd.Metadata.(*cran_module.Metadata)
  81. fmt.Fprintln(w, "Package:", pd.Package.Name)
  82. fmt.Fprintln(w, "Version:", pd.Version.Version)
  83. if metadata.License != "" {
  84. fmt.Fprintln(w, "License:", metadata.License)
  85. }
  86. if len(metadata.Depends) > 0 {
  87. fmt.Fprintln(w, "Depends:", strings.Join(metadata.Depends, ", "))
  88. }
  89. if len(metadata.Imports) > 0 {
  90. fmt.Fprintln(w, "Imports:", strings.Join(metadata.Imports, ", "))
  91. }
  92. if len(metadata.LinkingTo) > 0 {
  93. fmt.Fprintln(w, "LinkingTo:", strings.Join(metadata.LinkingTo, ", "))
  94. }
  95. if len(metadata.Suggests) > 0 {
  96. fmt.Fprintln(w, "Suggests:", strings.Join(metadata.Suggests, ", "))
  97. }
  98. needsCompilation := "no"
  99. if metadata.NeedsCompilation {
  100. needsCompilation = "yes"
  101. }
  102. fmt.Fprintln(w, "NeedsCompilation:", needsCompilation)
  103. fmt.Fprintln(w, "MD5sum:", pfd.Blob.HashMD5)
  104. }
  105. }
  106. func UploadSourcePackageFile(ctx *context.Context) {
  107. uploadPackageFile(
  108. ctx,
  109. packages_model.EmptyFileKey,
  110. map[string]string{
  111. cran_module.PropertyType: cran_module.TypeSource,
  112. },
  113. )
  114. }
  115. func UploadBinaryPackageFile(ctx *context.Context) {
  116. platform, rversion := ctx.FormTrim("platform"), ctx.FormTrim("rversion")
  117. if platform == "" || rversion == "" {
  118. apiError(ctx, http.StatusBadRequest, nil)
  119. return
  120. }
  121. uploadPackageFile(
  122. ctx,
  123. platform+"|"+rversion,
  124. map[string]string{
  125. cran_module.PropertyType: cran_module.TypeBinary,
  126. cran_module.PropertyPlatform: platform,
  127. cran_module.PropertyRVersion: rversion,
  128. },
  129. )
  130. }
  131. func uploadPackageFile(ctx *context.Context, compositeKey string, properties map[string]string) {
  132. upload, needToClose, err := ctx.UploadStream()
  133. if err != nil {
  134. apiError(ctx, http.StatusBadRequest, err)
  135. return
  136. }
  137. if needToClose {
  138. defer upload.Close()
  139. }
  140. buf, err := packages_module.CreateHashedBufferFromReader(upload)
  141. if err != nil {
  142. apiError(ctx, http.StatusInternalServerError, err)
  143. return
  144. }
  145. defer buf.Close()
  146. pck, err := cran_module.ParsePackage(buf, buf.Size())
  147. if err != nil {
  148. if errors.Is(err, util.ErrInvalidArgument) {
  149. apiError(ctx, http.StatusBadRequest, err)
  150. } else {
  151. apiError(ctx, http.StatusInternalServerError, err)
  152. }
  153. return
  154. }
  155. if _, err := buf.Seek(0, io.SeekStart); err != nil {
  156. apiError(ctx, http.StatusInternalServerError, err)
  157. return
  158. }
  159. _, _, err = packages_service.CreatePackageOrAddFileToExisting(
  160. ctx,
  161. &packages_service.PackageCreationInfo{
  162. PackageInfo: packages_service.PackageInfo{
  163. Owner: ctx.Package.Owner,
  164. PackageType: packages_model.TypeCran,
  165. Name: pck.Name,
  166. Version: pck.Version,
  167. },
  168. SemverCompatible: false,
  169. Creator: ctx.Doer,
  170. Metadata: pck.Metadata,
  171. },
  172. &packages_service.PackageFileCreationInfo{
  173. PackageFileInfo: packages_service.PackageFileInfo{
  174. Filename: fmt.Sprintf("%s_%s%s", pck.Name, pck.Version, pck.FileExtension),
  175. CompositeKey: compositeKey,
  176. },
  177. Creator: ctx.Doer,
  178. Data: buf,
  179. IsLead: true,
  180. Properties: properties,
  181. },
  182. )
  183. if err != nil {
  184. switch err {
  185. case packages_model.ErrDuplicatePackageFile:
  186. apiError(ctx, http.StatusConflict, err)
  187. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  188. apiError(ctx, http.StatusForbidden, err)
  189. default:
  190. apiError(ctx, http.StatusInternalServerError, err)
  191. }
  192. return
  193. }
  194. ctx.Status(http.StatusCreated)
  195. }
  196. func DownloadSourcePackageFile(ctx *context.Context) {
  197. downloadPackageFile(ctx, &cran_model.SearchOptions{
  198. OwnerID: ctx.Package.Owner.ID,
  199. FileType: cran_module.TypeSource,
  200. Filename: ctx.PathParam("filename"),
  201. })
  202. }
  203. func DownloadBinaryPackageFile(ctx *context.Context) {
  204. downloadPackageFile(ctx, &cran_model.SearchOptions{
  205. OwnerID: ctx.Package.Owner.ID,
  206. FileType: cran_module.TypeBinary,
  207. Platform: ctx.PathParam("platform"),
  208. RVersion: ctx.PathParam("rversion"),
  209. Filename: ctx.PathParam("filename"),
  210. })
  211. }
  212. func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
  213. pf, err := cran_model.SearchFile(ctx, opts)
  214. if err != nil {
  215. if errors.Is(err, util.ErrNotExist) {
  216. apiError(ctx, http.StatusNotFound, err)
  217. } else {
  218. apiError(ctx, http.StatusInternalServerError, err)
  219. }
  220. return
  221. }
  222. s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method)
  223. if err != nil {
  224. if errors.Is(err, util.ErrNotExist) {
  225. apiError(ctx, http.StatusNotFound, err)
  226. } else {
  227. apiError(ctx, http.StatusInternalServerError, err)
  228. }
  229. return
  230. }
  231. helper.ServePackageFile(ctx, s, u, pf)
  232. }