gitea源码

search.go 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package conan
  4. import (
  5. "errors"
  6. "net/http"
  7. "strings"
  8. conan_model "code.gitea.io/gitea/models/packages/conan"
  9. user_model "code.gitea.io/gitea/models/user"
  10. "code.gitea.io/gitea/modules/json"
  11. conan_module "code.gitea.io/gitea/modules/packages/conan"
  12. "code.gitea.io/gitea/services/context"
  13. )
  14. // SearchResult contains the found recipe names
  15. type SearchResult struct {
  16. Results []string `json:"results"`
  17. }
  18. // SearchRecipes searches all recipes matching the query
  19. func SearchRecipes(ctx *context.Context) {
  20. q := ctx.FormTrim("q")
  21. opts := parseQuery(ctx.Package.Owner, q)
  22. results, err := conan_model.SearchRecipes(ctx, opts)
  23. if err != nil {
  24. apiError(ctx, http.StatusInternalServerError, err)
  25. return
  26. }
  27. jsonResponse(ctx, http.StatusOK, &SearchResult{
  28. Results: results,
  29. })
  30. }
  31. // parseQuery creates search options for the given query
  32. func parseQuery(owner *user_model.User, query string) *conan_model.RecipeSearchOptions {
  33. opts := &conan_model.RecipeSearchOptions{
  34. OwnerID: owner.ID,
  35. }
  36. if query != "" {
  37. parts := strings.Split(strings.ReplaceAll(query, "@", "/"), "/")
  38. opts.Name = parts[0]
  39. if len(parts) > 1 && parts[1] != "*" {
  40. opts.Version = parts[1]
  41. }
  42. if len(parts) > 2 && parts[2] != "*" {
  43. opts.User = parts[2]
  44. }
  45. if len(parts) > 3 && parts[3] != "*" {
  46. opts.Channel = parts[3]
  47. }
  48. }
  49. return opts
  50. }
  51. // SearchPackagesV1 searches all packages of a recipe (Conan v1 endpoint)
  52. func SearchPackagesV1(ctx *context.Context) {
  53. searchPackages(ctx, true)
  54. }
  55. // SearchPackagesV2 searches all packages of a recipe (Conan v2 endpoint)
  56. func SearchPackagesV2(ctx *context.Context) {
  57. searchPackages(ctx, false)
  58. }
  59. func searchPackages(ctx *context.Context, searchAllRevisions bool) {
  60. rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
  61. if !searchAllRevisions && rref.Revision == "" {
  62. lastRevision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
  63. if err != nil {
  64. if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
  65. apiError(ctx, http.StatusNotFound, err)
  66. } else {
  67. apiError(ctx, http.StatusInternalServerError, err)
  68. }
  69. return
  70. }
  71. rref = rref.WithRevision(lastRevision.Value)
  72. } else {
  73. has, err := conan_model.RecipeExists(ctx, ctx.Package.Owner.ID, rref)
  74. if err != nil {
  75. if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
  76. apiError(ctx, http.StatusNotFound, err)
  77. } else {
  78. apiError(ctx, http.StatusInternalServerError, err)
  79. }
  80. return
  81. }
  82. if !has {
  83. apiError(ctx, http.StatusNotFound, nil)
  84. return
  85. }
  86. }
  87. recipeRevisions := []*conan_model.PropertyValue{{Value: rref.Revision}}
  88. if searchAllRevisions {
  89. var err error
  90. recipeRevisions, err = conan_model.GetRecipeRevisions(ctx, ctx.Package.Owner.ID, rref)
  91. if err != nil {
  92. apiError(ctx, http.StatusInternalServerError, err)
  93. return
  94. }
  95. }
  96. result := make(map[string]*conan_module.Conaninfo)
  97. for _, recipeRevision := range recipeRevisions {
  98. currentRef := rref
  99. if recipeRevision.Value != "" {
  100. currentRef = rref.WithRevision(recipeRevision.Value)
  101. }
  102. packageReferences, err := conan_model.GetPackageReferences(ctx, ctx.Package.Owner.ID, currentRef)
  103. if err != nil {
  104. if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
  105. apiError(ctx, http.StatusNotFound, err)
  106. } else {
  107. apiError(ctx, http.StatusInternalServerError, err)
  108. }
  109. return
  110. }
  111. for _, packageReference := range packageReferences {
  112. if _, ok := result[packageReference.Value]; ok {
  113. continue
  114. }
  115. pref, _ := conan_module.NewPackageReference(currentRef, packageReference.Value, "")
  116. lastPackageRevision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
  117. if err != nil {
  118. if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
  119. apiError(ctx, http.StatusNotFound, err)
  120. } else {
  121. apiError(ctx, http.StatusInternalServerError, err)
  122. }
  123. return
  124. }
  125. pref = pref.WithRevision(lastPackageRevision.Value)
  126. infoRaw, err := conan_model.GetPackageInfo(ctx, ctx.Package.Owner.ID, pref)
  127. if err != nil {
  128. if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
  129. apiError(ctx, http.StatusNotFound, err)
  130. } else {
  131. apiError(ctx, http.StatusInternalServerError, err)
  132. }
  133. return
  134. }
  135. var info *conan_module.Conaninfo
  136. if err := json.Unmarshal([]byte(infoRaw), &info); err != nil {
  137. apiError(ctx, http.StatusInternalServerError, err)
  138. return
  139. }
  140. result[pref.Reference] = info
  141. }
  142. }
  143. jsonResponse(ctx, http.StatusOK, result)
  144. }