gitea源码

package.go 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package user
  4. import (
  5. gocontext "context"
  6. "errors"
  7. "net/http"
  8. "net/url"
  9. "code.gitea.io/gitea/models/db"
  10. org_model "code.gitea.io/gitea/models/organization"
  11. packages_model "code.gitea.io/gitea/models/packages"
  12. container_model "code.gitea.io/gitea/models/packages/container"
  13. "code.gitea.io/gitea/models/perm"
  14. access_model "code.gitea.io/gitea/models/perm/access"
  15. repo_model "code.gitea.io/gitea/models/repo"
  16. "code.gitea.io/gitea/modules/container"
  17. "code.gitea.io/gitea/modules/httplib"
  18. "code.gitea.io/gitea/modules/log"
  19. "code.gitea.io/gitea/modules/optional"
  20. alpine_module "code.gitea.io/gitea/modules/packages/alpine"
  21. arch_module "code.gitea.io/gitea/modules/packages/arch"
  22. container_module "code.gitea.io/gitea/modules/packages/container"
  23. debian_module "code.gitea.io/gitea/modules/packages/debian"
  24. rpm_module "code.gitea.io/gitea/modules/packages/rpm"
  25. "code.gitea.io/gitea/modules/setting"
  26. "code.gitea.io/gitea/modules/templates"
  27. "code.gitea.io/gitea/modules/util"
  28. "code.gitea.io/gitea/modules/web"
  29. packages_helper "code.gitea.io/gitea/routers/api/packages/helper"
  30. shared_user "code.gitea.io/gitea/routers/web/shared/user"
  31. "code.gitea.io/gitea/services/context"
  32. "code.gitea.io/gitea/services/forms"
  33. packages_service "code.gitea.io/gitea/services/packages"
  34. container_service "code.gitea.io/gitea/services/packages/container"
  35. )
  36. const (
  37. tplPackagesList templates.TplName = "user/overview/packages"
  38. tplPackagesView templates.TplName = "package/view"
  39. tplPackageVersionList templates.TplName = "user/overview/package_versions"
  40. tplPackagesSettings templates.TplName = "package/settings"
  41. )
  42. // ListPackages displays a list of all packages of the context user
  43. func ListPackages(ctx *context.Context) {
  44. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  45. ctx.ServerError("RenderUserOrgHeader", err)
  46. return
  47. }
  48. page := max(ctx.FormInt("page"), 1)
  49. query := ctx.FormTrim("q")
  50. packageType := ctx.FormTrim("type")
  51. pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  52. Paginator: &db.ListOptions{
  53. PageSize: setting.UI.PackagesPagingNum,
  54. Page: page,
  55. },
  56. OwnerID: ctx.ContextUser.ID,
  57. Type: packages_model.Type(packageType),
  58. Name: packages_model.SearchValue{Value: query},
  59. IsInternal: optional.Some(false),
  60. })
  61. if err != nil {
  62. ctx.ServerError("SearchLatestVersions", err)
  63. return
  64. }
  65. pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
  66. if err != nil {
  67. ctx.ServerError("GetPackageDescriptors", err)
  68. return
  69. }
  70. repositoryAccessMap := make(map[int64]bool)
  71. for _, pd := range pds {
  72. if pd.Repository == nil {
  73. continue
  74. }
  75. if _, has := repositoryAccessMap[pd.Repository.ID]; has {
  76. continue
  77. }
  78. permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
  79. if err != nil {
  80. ctx.ServerError("GetUserRepoPermission", err)
  81. return
  82. }
  83. repositoryAccessMap[pd.Repository.ID] = permission.HasAnyUnitAccess()
  84. }
  85. hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID)
  86. if err != nil {
  87. ctx.ServerError("HasOwnerPackages", err)
  88. return
  89. }
  90. ctx.Data["Title"] = ctx.Tr("packages.title")
  91. ctx.Data["IsPackagesPage"] = true
  92. ctx.Data["Query"] = query
  93. ctx.Data["PackageType"] = packageType
  94. ctx.Data["AvailableTypes"] = packages_model.TypeList
  95. ctx.Data["HasPackages"] = hasPackages
  96. ctx.Data["PackageDescriptors"] = pds
  97. ctx.Data["Total"] = total
  98. ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
  99. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  100. ctx.ServerError("RenderUserOrgHeader", err)
  101. return
  102. }
  103. // TODO: context/org -> HandleOrgAssignment() can not be used
  104. if ctx.ContextUser.IsOrganization() {
  105. org := org_model.OrgFromUser(ctx.ContextUser)
  106. ctx.Data["Org"] = org
  107. ctx.Data["OrgLink"] = ctx.ContextUser.OrganisationLink()
  108. if ctx.Doer != nil {
  109. ctx.Data["IsOrganizationMember"], _ = org_model.IsOrganizationMember(ctx, org.ID, ctx.Doer.ID)
  110. ctx.Data["IsOrganizationOwner"], _ = org_model.IsOrganizationOwner(ctx, org.ID, ctx.Doer.ID)
  111. } else {
  112. ctx.Data["IsOrganizationMember"] = false
  113. ctx.Data["IsOrganizationOwner"] = false
  114. }
  115. }
  116. pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
  117. pager.AddParamFromRequest(ctx.Req)
  118. ctx.Data["Page"] = pager
  119. ctx.HTML(http.StatusOK, tplPackagesList)
  120. }
  121. // RedirectToLastVersion redirects to the latest package version
  122. func RedirectToLastVersion(ctx *context.Context) {
  123. p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
  124. if err != nil {
  125. if err == packages_model.ErrPackageNotExist {
  126. ctx.NotFound(err)
  127. } else {
  128. ctx.ServerError("GetPackageByName", err)
  129. }
  130. return
  131. }
  132. pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
  133. PackageID: p.ID,
  134. IsInternal: optional.Some(false),
  135. })
  136. if err != nil {
  137. ctx.ServerError("GetPackageByName", err)
  138. return
  139. }
  140. if len(pvs) == 0 {
  141. ctx.NotFound(err)
  142. return
  143. }
  144. pd, err := packages_model.GetPackageDescriptor(ctx, pvs[0])
  145. if err != nil {
  146. ctx.ServerError("GetPackageDescriptor", err)
  147. return
  148. }
  149. ctx.Redirect(pd.VersionWebLink())
  150. }
  151. func viewPackageContainerImage(ctx gocontext.Context, pd *packages_model.PackageDescriptor, digest string) (*container_module.Metadata, error) {
  152. manifestBlob, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
  153. OwnerID: pd.Owner.ID,
  154. Image: pd.Package.LowerName,
  155. Digest: digest,
  156. })
  157. if err != nil {
  158. return nil, err
  159. }
  160. manifestReader, err := packages_service.OpenBlobStream(manifestBlob.Blob)
  161. if err != nil {
  162. return nil, err
  163. }
  164. defer manifestReader.Close()
  165. _, _, metadata, err := container_service.ParseManifestMetadata(ctx, manifestReader, pd.Owner.ID, pd.Package.LowerName)
  166. return metadata, err
  167. }
  168. // ViewPackageVersion displays a single package version
  169. func ViewPackageVersion(ctx *context.Context) {
  170. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  171. ctx.ServerError("RenderUserOrgHeader", err)
  172. return
  173. }
  174. versionSub := ctx.PathParam("version_sub")
  175. pd := ctx.Package.Descriptor
  176. ctx.Data["Title"] = pd.Package.Name
  177. ctx.Data["IsPackagesPage"] = true
  178. ctx.Data["PackageDescriptor"] = pd
  179. registryHostURL, err := url.Parse(httplib.GuessCurrentHostURL(ctx))
  180. if err != nil {
  181. registryHostURL, _ = url.Parse(setting.AppURL)
  182. }
  183. ctx.Data["PackageRegistryHost"] = registryHostURL.Host
  184. switch pd.Package.Type {
  185. case packages_model.TypeAlpine:
  186. branches := make(container.Set[string])
  187. repositories := make(container.Set[string])
  188. architectures := make(container.Set[string])
  189. for _, f := range pd.Files {
  190. for _, pp := range f.Properties {
  191. switch pp.Name {
  192. case alpine_module.PropertyBranch:
  193. branches.Add(pp.Value)
  194. case alpine_module.PropertyRepository:
  195. repositories.Add(pp.Value)
  196. case alpine_module.PropertyArchitecture:
  197. architectures.Add(pp.Value)
  198. }
  199. }
  200. }
  201. ctx.Data["Branches"] = util.Sorted(branches.Values())
  202. ctx.Data["Repositories"] = util.Sorted(repositories.Values())
  203. ctx.Data["Architectures"] = util.Sorted(architectures.Values())
  204. case packages_model.TypeArch:
  205. repositories := make(container.Set[string])
  206. architectures := make(container.Set[string])
  207. for _, f := range pd.Files {
  208. for _, pp := range f.Properties {
  209. switch pp.Name {
  210. case arch_module.PropertyRepository:
  211. repositories.Add(pp.Value)
  212. case arch_module.PropertyArchitecture:
  213. architectures.Add(pp.Value)
  214. }
  215. }
  216. }
  217. ctx.Data["Repositories"] = util.Sorted(repositories.Values())
  218. ctx.Data["Architectures"] = util.Sorted(architectures.Values())
  219. case packages_model.TypeDebian:
  220. distributions := make(container.Set[string])
  221. components := make(container.Set[string])
  222. architectures := make(container.Set[string])
  223. for _, f := range pd.Files {
  224. for _, pp := range f.Properties {
  225. switch pp.Name {
  226. case debian_module.PropertyDistribution:
  227. distributions.Add(pp.Value)
  228. case debian_module.PropertyComponent:
  229. components.Add(pp.Value)
  230. case debian_module.PropertyArchitecture:
  231. architectures.Add(pp.Value)
  232. }
  233. }
  234. }
  235. ctx.Data["Distributions"] = util.Sorted(distributions.Values())
  236. ctx.Data["Components"] = util.Sorted(components.Values())
  237. ctx.Data["Architectures"] = util.Sorted(architectures.Values())
  238. case packages_model.TypeRpm:
  239. groups := make(container.Set[string])
  240. architectures := make(container.Set[string])
  241. for _, f := range pd.Files {
  242. for _, pp := range f.Properties {
  243. switch pp.Name {
  244. case rpm_module.PropertyGroup:
  245. groups.Add(pp.Value)
  246. case rpm_module.PropertyArchitecture:
  247. architectures.Add(pp.Value)
  248. }
  249. }
  250. }
  251. ctx.Data["Groups"] = util.Sorted(groups.Values())
  252. ctx.Data["Architectures"] = util.Sorted(architectures.Values())
  253. case packages_model.TypeContainer:
  254. imageMetadata := pd.Metadata
  255. if versionSub != "" {
  256. imageMetadata, err = viewPackageContainerImage(ctx, pd, versionSub)
  257. if errors.Is(err, util.ErrNotExist) {
  258. ctx.NotFound(nil)
  259. return
  260. } else if err != nil {
  261. ctx.ServerError("viewPackageContainerImage", err)
  262. return
  263. }
  264. }
  265. ctx.Data["ContainerImageMetadata"] = imageMetadata
  266. }
  267. var pvs []*packages_model.PackageVersion
  268. var pvsTotal int64
  269. if pd.Package.Type == packages_model.TypeContainer {
  270. pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
  271. Paginator: db.NewAbsoluteListOptions(0, 5),
  272. PackageID: pd.Package.ID,
  273. IsTagged: true,
  274. })
  275. } else {
  276. pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  277. Paginator: db.NewAbsoluteListOptions(0, 5),
  278. PackageID: pd.Package.ID,
  279. IsInternal: optional.Some(false),
  280. })
  281. }
  282. if err != nil {
  283. ctx.ServerError("", err)
  284. return
  285. }
  286. ctx.Data["LatestVersions"] = pvs
  287. ctx.Data["TotalVersionCount"] = pvsTotal
  288. ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
  289. hasRepositoryAccess := false
  290. if pd.Repository != nil {
  291. permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
  292. if err != nil {
  293. ctx.ServerError("GetUserRepoPermission", err)
  294. return
  295. }
  296. hasRepositoryAccess = permission.HasAnyUnitAccess()
  297. }
  298. ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
  299. ctx.HTML(http.StatusOK, tplPackagesView)
  300. }
  301. // ListPackageVersions lists all versions of a package
  302. func ListPackageVersions(ctx *context.Context) {
  303. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  304. ctx.ServerError("RenderUserOrgHeader", err)
  305. return
  306. }
  307. p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
  308. if err != nil {
  309. if err == packages_model.ErrPackageNotExist {
  310. ctx.NotFound(err)
  311. } else {
  312. ctx.ServerError("GetPackageByName", err)
  313. }
  314. return
  315. }
  316. page := max(ctx.FormInt("page"), 1)
  317. pagination := &db.ListOptions{
  318. PageSize: setting.UI.PackagesPagingNum,
  319. Page: page,
  320. }
  321. query := ctx.FormTrim("q")
  322. sort := ctx.FormTrim("sort")
  323. ctx.Data["Title"] = ctx.Tr("packages.title")
  324. ctx.Data["IsPackagesPage"] = true
  325. ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
  326. Package: p,
  327. Owner: ctx.Package.Owner,
  328. }
  329. ctx.Data["Query"] = query
  330. ctx.Data["Sort"] = sort
  331. var (
  332. total int64
  333. pvs []*packages_model.PackageVersion
  334. )
  335. switch p.Type {
  336. case packages_model.TypeContainer:
  337. tagged := ctx.FormTrim("tagged")
  338. ctx.Data["Tagged"] = tagged
  339. pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
  340. Paginator: pagination,
  341. PackageID: p.ID,
  342. Query: query,
  343. IsTagged: tagged == "" || tagged == "tagged",
  344. Sort: sort,
  345. })
  346. if err != nil {
  347. ctx.ServerError("SearchImageTags", err)
  348. return
  349. }
  350. default:
  351. pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
  352. Paginator: pagination,
  353. PackageID: p.ID,
  354. Version: packages_model.SearchValue{
  355. ExactMatch: false,
  356. Value: query,
  357. },
  358. IsInternal: optional.Some(false),
  359. Sort: sort,
  360. })
  361. if err != nil {
  362. ctx.ServerError("SearchVersions", err)
  363. return
  364. }
  365. }
  366. ctx.Data["PackageDescriptors"], err = packages_model.GetPackageDescriptors(ctx, pvs)
  367. if err != nil {
  368. ctx.ServerError("GetPackageDescriptors", err)
  369. return
  370. }
  371. ctx.Data["Total"] = total
  372. pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
  373. pager.AddParamFromRequest(ctx.Req)
  374. ctx.Data["Page"] = pager
  375. ctx.HTML(http.StatusOK, tplPackageVersionList)
  376. }
  377. // PackageSettings displays the package settings page
  378. func PackageSettings(ctx *context.Context) {
  379. pd := ctx.Package.Descriptor
  380. if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
  381. ctx.ServerError("RenderUserOrgHeader", err)
  382. return
  383. }
  384. ctx.Data["Title"] = pd.Package.Name
  385. ctx.Data["IsPackagesPage"] = true
  386. ctx.Data["PackageDescriptor"] = pd
  387. ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
  388. if pd.Package.RepoID > 0 {
  389. repo, err := repo_model.GetRepositoryByID(ctx, pd.Package.RepoID)
  390. if err != nil {
  391. ctx.ServerError("GetRepositoryByID", err)
  392. return
  393. }
  394. ctx.Data["LinkedRepoName"] = repo.Name
  395. }
  396. ctx.HTML(http.StatusOK, tplPackagesSettings)
  397. }
  398. // PackageSettingsPost updates the package settings
  399. func PackageSettingsPost(ctx *context.Context) {
  400. form := web.GetForm(ctx).(*forms.PackageSettingForm)
  401. switch form.Action {
  402. case "link":
  403. packageSettingsPostActionLink(ctx, form)
  404. case "delete":
  405. packageSettingsPostActionDelete(ctx)
  406. default:
  407. ctx.NotFound(nil)
  408. }
  409. }
  410. func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSettingForm) {
  411. pd := ctx.Package.Descriptor
  412. if form.RepoName == "" { // remove the link
  413. if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, 0); err != nil {
  414. ctx.JSONError(ctx.Tr("packages.settings.unlink.error"))
  415. return
  416. }
  417. ctx.Flash.Success(ctx.Tr("packages.settings.unlink.success"))
  418. ctx.JSONRedirect("")
  419. return
  420. }
  421. repo, err := repo_model.GetRepositoryByName(ctx, pd.Owner.ID, form.RepoName)
  422. if err != nil {
  423. if repo_model.IsErrRepoNotExist(err) {
  424. ctx.JSONError(ctx.Tr("packages.settings.link.repo_not_found", form.RepoName))
  425. } else {
  426. ctx.ServerError("GetRepositoryByOwnerAndName", err)
  427. }
  428. return
  429. }
  430. if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repo.ID); err != nil {
  431. ctx.JSONError(ctx.Tr("packages.settings.link.error"))
  432. return
  433. }
  434. ctx.Flash.Success(ctx.Tr("packages.settings.link.success"))
  435. ctx.JSONRedirect("")
  436. }
  437. func packageSettingsPostActionDelete(ctx *context.Context) {
  438. err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
  439. if err != nil {
  440. log.Error("Error deleting package: %v", err)
  441. ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
  442. } else {
  443. ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
  444. }
  445. redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
  446. // redirect to the package if there are still versions available
  447. if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has {
  448. redirectURL = ctx.Package.Descriptor.PackageWebLink()
  449. }
  450. ctx.Redirect(redirectURL)
  451. }
  452. // DownloadPackageFile serves the content of a package file
  453. func DownloadPackageFile(ctx *context.Context) {
  454. pf, err := packages_model.GetFileForVersionByID(ctx, ctx.Package.Descriptor.Version.ID, ctx.PathParamInt64("fileid"))
  455. if err != nil {
  456. if err == packages_model.ErrPackageFileNotExist {
  457. ctx.NotFound(err)
  458. } else {
  459. ctx.ServerError("GetFileForVersionByID", err)
  460. }
  461. return
  462. }
  463. s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method)
  464. if err != nil {
  465. ctx.ServerError("OpenFileForDownload", err)
  466. return
  467. }
  468. packages_helper.ServePackageFile(ctx, s, u, pf)
  469. }