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.

package.go 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. // Copyright 2021 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 user
  5. import (
  6. "net/http"
  7. "code.gitea.io/gitea/models/db"
  8. packages_model "code.gitea.io/gitea/models/packages"
  9. container_model "code.gitea.io/gitea/models/packages/container"
  10. "code.gitea.io/gitea/models/perm"
  11. access_model "code.gitea.io/gitea/models/perm/access"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/util"
  18. "code.gitea.io/gitea/modules/web"
  19. "code.gitea.io/gitea/services/forms"
  20. packages_service "code.gitea.io/gitea/services/packages"
  21. )
  22. const (
  23. tplPackagesList base.TplName = "user/overview/packages"
  24. tplPackagesView base.TplName = "package/view"
  25. tplPackageVersionList base.TplName = "user/overview/package_versions"
  26. tplPackagesSettings base.TplName = "package/settings"
  27. )
  28. // ListPackages displays a list of all packages of the context user
  29. func ListPackages(ctx *context.Context) {
  30. page := ctx.FormInt("page")
  31. if page <= 1 {
  32. page = 1
  33. }
  34. query := ctx.FormTrim("q")
  35. packageType := ctx.FormTrim("type")
  36. pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  37. Paginator: &db.ListOptions{
  38. PageSize: setting.UI.PackagesPagingNum,
  39. Page: page,
  40. },
  41. OwnerID: ctx.ContextUser.ID,
  42. Type: packages_model.Type(packageType),
  43. Name: packages_model.SearchValue{Value: query},
  44. IsInternal: util.OptionalBoolFalse,
  45. })
  46. if err != nil {
  47. ctx.ServerError("SearchLatestVersions", err)
  48. return
  49. }
  50. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  51. if err != nil {
  52. ctx.ServerError("GetPackageDescriptors", err)
  53. return
  54. }
  55. repositoryAccessMap := make(map[int64]bool)
  56. for _, pd := range pds {
  57. if pd.Repository == nil {
  58. continue
  59. }
  60. if _, has := repositoryAccessMap[pd.Repository.ID]; has {
  61. continue
  62. }
  63. permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
  64. if err != nil {
  65. ctx.ServerError("GetUserRepoPermission", err)
  66. return
  67. }
  68. repositoryAccessMap[pd.Repository.ID] = permission.HasAccess()
  69. }
  70. hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID)
  71. if err != nil {
  72. ctx.ServerError("HasOwnerPackages", err)
  73. return
  74. }
  75. ctx.Data["Title"] = ctx.Tr("packages.title")
  76. ctx.Data["IsPackagesPage"] = true
  77. ctx.Data["ContextUser"] = ctx.ContextUser
  78. ctx.Data["Query"] = query
  79. ctx.Data["PackageType"] = packageType
  80. ctx.Data["HasPackages"] = hasPackages
  81. ctx.Data["PackageDescriptors"] = pds
  82. ctx.Data["Total"] = total
  83. ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
  84. pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
  85. pager.AddParam(ctx, "q", "Query")
  86. pager.AddParam(ctx, "type", "PackageType")
  87. ctx.Data["Page"] = pager
  88. ctx.HTML(http.StatusOK, tplPackagesList)
  89. }
  90. // RedirectToLastVersion redirects to the latest package version
  91. func RedirectToLastVersion(ctx *context.Context) {
  92. p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.Params("type")), ctx.Params("name"))
  93. if err != nil {
  94. if err == packages_model.ErrPackageNotExist {
  95. ctx.NotFound("GetPackageByName", err)
  96. } else {
  97. ctx.ServerError("GetPackageByName", err)
  98. }
  99. return
  100. }
  101. pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  102. PackageID: p.ID,
  103. IsInternal: util.OptionalBoolFalse,
  104. })
  105. if err != nil {
  106. ctx.ServerError("GetPackageByName", err)
  107. return
  108. }
  109. if len(pvs) == 0 {
  110. ctx.NotFound("", err)
  111. return
  112. }
  113. pd, err := packages_model.GetPackageDescriptor(ctx, pvs[0])
  114. if err != nil {
  115. ctx.ServerError("GetPackageDescriptor", err)
  116. return
  117. }
  118. ctx.Redirect(pd.FullWebLink())
  119. }
  120. // ViewPackageVersion displays a single package version
  121. func ViewPackageVersion(ctx *context.Context) {
  122. pd := ctx.Package.Descriptor
  123. ctx.Data["Title"] = pd.Package.Name
  124. ctx.Data["IsPackagesPage"] = true
  125. ctx.Data["ContextUser"] = ctx.ContextUser
  126. ctx.Data["PackageDescriptor"] = pd
  127. var (
  128. total int64
  129. pvs []*packages_model.PackageVersion
  130. err error
  131. )
  132. switch pd.Package.Type {
  133. case packages_model.TypeContainer:
  134. ctx.Data["RegistryHost"] = setting.Packages.RegistryHost
  135. pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
  136. Paginator: db.NewAbsoluteListOptions(0, 5),
  137. PackageID: pd.Package.ID,
  138. IsTagged: true,
  139. })
  140. default:
  141. pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  142. Paginator: db.NewAbsoluteListOptions(0, 5),
  143. PackageID: pd.Package.ID,
  144. IsInternal: util.OptionalBoolFalse,
  145. })
  146. if err != nil {
  147. ctx.ServerError("SearchVersions", err)
  148. return
  149. }
  150. }
  151. if err != nil {
  152. ctx.ServerError("", err)
  153. return
  154. }
  155. ctx.Data["LatestVersions"] = pvs
  156. ctx.Data["TotalVersionCount"] = total
  157. ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
  158. hasRepositoryAccess := false
  159. if pd.Repository != nil {
  160. permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
  161. if err != nil {
  162. ctx.ServerError("GetUserRepoPermission", err)
  163. return
  164. }
  165. hasRepositoryAccess = permission.HasAccess()
  166. }
  167. ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
  168. ctx.HTML(http.StatusOK, tplPackagesView)
  169. }
  170. // ListPackageVersions lists all versions of a package
  171. func ListPackageVersions(ctx *context.Context) {
  172. p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.Params("type")), ctx.Params("name"))
  173. if err != nil {
  174. if err == packages_model.ErrPackageNotExist {
  175. ctx.NotFound("GetPackageByName", err)
  176. } else {
  177. ctx.ServerError("GetPackageByName", err)
  178. }
  179. return
  180. }
  181. page := ctx.FormInt("page")
  182. if page <= 1 {
  183. page = 1
  184. }
  185. pagination := &db.ListOptions{
  186. PageSize: setting.UI.PackagesPagingNum,
  187. Page: page,
  188. }
  189. query := ctx.FormTrim("q")
  190. ctx.Data["Title"] = ctx.Tr("packages.title")
  191. ctx.Data["IsPackagesPage"] = true
  192. ctx.Data["ContextUser"] = ctx.ContextUser
  193. ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
  194. Package: p,
  195. Owner: ctx.Package.Owner,
  196. }
  197. ctx.Data["Query"] = query
  198. pagerParams := map[string]string{
  199. "q": query,
  200. }
  201. var (
  202. total int64
  203. pvs []*packages_model.PackageVersion
  204. )
  205. switch p.Type {
  206. case packages_model.TypeContainer:
  207. tagged := ctx.FormTrim("tagged")
  208. pagerParams["tagged"] = tagged
  209. ctx.Data["Tagged"] = tagged
  210. pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
  211. Paginator: pagination,
  212. PackageID: p.ID,
  213. Query: query,
  214. IsTagged: tagged == "" || tagged == "tagged",
  215. })
  216. if err != nil {
  217. ctx.ServerError("SearchImageTags", err)
  218. return
  219. }
  220. default:
  221. pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  222. Paginator: pagination,
  223. PackageID: p.ID,
  224. Version: packages_model.SearchValue{
  225. ExactMatch: false,
  226. Value: query,
  227. },
  228. IsInternal: util.OptionalBoolFalse,
  229. })
  230. if err != nil {
  231. ctx.ServerError("SearchVersions", err)
  232. return
  233. }
  234. }
  235. ctx.Data["PackageDescriptors"], err = packages_model.GetPackageDescriptors(ctx, pvs)
  236. if err != nil {
  237. ctx.ServerError("GetPackageDescriptors", err)
  238. return
  239. }
  240. ctx.Data["Total"] = total
  241. pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
  242. for k, v := range pagerParams {
  243. pager.AddParamString(k, v)
  244. }
  245. ctx.Data["Page"] = pager
  246. ctx.HTML(http.StatusOK, tplPackageVersionList)
  247. }
  248. // PackageSettings displays the package settings page
  249. func PackageSettings(ctx *context.Context) {
  250. pd := ctx.Package.Descriptor
  251. ctx.Data["Title"] = pd.Package.Name
  252. ctx.Data["IsPackagesPage"] = true
  253. ctx.Data["ContextUser"] = ctx.ContextUser
  254. ctx.Data["PackageDescriptor"] = pd
  255. repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
  256. Actor: pd.Owner,
  257. Private: true,
  258. })
  259. ctx.Data["Repos"] = repos
  260. ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
  261. ctx.HTML(http.StatusOK, tplPackagesSettings)
  262. }
  263. // PackageSettingsPost updates the package settings
  264. func PackageSettingsPost(ctx *context.Context) {
  265. pd := ctx.Package.Descriptor
  266. form := web.GetForm(ctx).(*forms.PackageSettingForm)
  267. switch form.Action {
  268. case "link":
  269. success := func() bool {
  270. repoID := int64(0)
  271. if form.RepoID != 0 {
  272. repo, err := repo_model.GetRepositoryByID(form.RepoID)
  273. if err != nil {
  274. log.Error("Error getting repository: %v", err)
  275. return false
  276. }
  277. if repo.OwnerID != pd.Owner.ID {
  278. return false
  279. }
  280. repoID = repo.ID
  281. }
  282. if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repoID); err != nil {
  283. log.Error("Error updating package: %v", err)
  284. return false
  285. }
  286. return true
  287. }()
  288. if success {
  289. ctx.Flash.Success(ctx.Tr("packages.settings.link.success"))
  290. } else {
  291. ctx.Flash.Error(ctx.Tr("packages.settings.link.error"))
  292. }
  293. ctx.Redirect(ctx.Link)
  294. return
  295. case "delete":
  296. err := packages_service.RemovePackageVersion(ctx.Doer, ctx.Package.Descriptor.Version)
  297. if err != nil {
  298. log.Error("Error deleting package: %v", err)
  299. ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
  300. } else {
  301. ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
  302. }
  303. ctx.Redirect(ctx.Package.Owner.HTMLURL() + "/-/packages")
  304. return
  305. }
  306. }
  307. // DownloadPackageFile serves the content of a package file
  308. func DownloadPackageFile(ctx *context.Context) {
  309. pf, err := packages_model.GetFileForVersionByID(ctx, ctx.Package.Descriptor.Version.ID, ctx.ParamsInt64(":fileid"))
  310. if err != nil {
  311. if err == packages_model.ErrPackageFileNotExist {
  312. ctx.NotFound("", err)
  313. } else {
  314. ctx.ServerError("GetFileForVersionByID", err)
  315. }
  316. return
  317. }
  318. s, _, err := packages_service.GetPackageFileStream(
  319. ctx,
  320. pf,
  321. )
  322. if err != nil {
  323. ctx.ServerError("GetPackageFileStream", err)
  324. return
  325. }
  326. defer s.Close()
  327. ctx.ServeStream(s, pf.Name)
  328. }