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 11KB

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