2022-03-30 10:42:47 +02:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2022-03-30 10:42:47 +02:00
package user
import (
"net/http"
2024-06-09 16:29:29 +08:00
"net/url"
2022-03-30 10:42:47 +02:00
"code.gitea.io/gitea/models/db"
2022-07-31 14:32:51 +02:00
org_model "code.gitea.io/gitea/models/organization"
2022-03-30 10:42:47 +02:00
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/models/perm"
2022-05-11 18:09:36 +08:00
access_model "code.gitea.io/gitea/models/perm/access"
2022-03-30 10:42:47 +02:00
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
2023-05-02 18:31:35 +02:00
"code.gitea.io/gitea/modules/container"
2024-06-09 16:29:29 +08:00
"code.gitea.io/gitea/modules/httplib"
2022-03-30 10:42:47 +02:00
"code.gitea.io/gitea/modules/log"
2024-03-02 16:42:31 +01:00
"code.gitea.io/gitea/modules/optional"
2023-05-12 19:27:50 +02:00
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
2024-12-05 00:09:07 +01:00
arch_module "code.gitea.io/gitea/modules/packages/arch"
2023-05-02 18:31:35 +02:00
debian_module "code.gitea.io/gitea/modules/packages/debian"
2024-01-19 12:37:10 +01:00
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
2022-03-30 10:42:47 +02:00
"code.gitea.io/gitea/modules/setting"
2022-07-27 03:59:10 +02:00
"code.gitea.io/gitea/modules/util"
2022-03-30 10:42:47 +02:00
"code.gitea.io/gitea/modules/web"
2023-07-03 15:33:28 +02:00
packages_helper "code.gitea.io/gitea/routers/api/packages/helper"
2023-01-20 19:42:33 +08:00
shared_user "code.gitea.io/gitea/routers/web/shared/user"
2024-02-27 15:12:22 +08:00
"code.gitea.io/gitea/services/context"
2022-03-30 10:42:47 +02:00
"code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages"
)
const (
tplPackagesList base . TplName = "user/overview/packages"
tplPackagesView base . TplName = "package/view"
tplPackageVersionList base . TplName = "user/overview/package_versions"
tplPackagesSettings base . TplName = "package/settings"
)
// ListPackages displays a list of all packages of the context user
func ListPackages ( ctx * context . Context ) {
2023-07-07 00:29:24 +05:30
shared_user . PrepareContextForProfileBigAvatar ( ctx )
2022-03-30 10:42:47 +02:00
page := ctx . FormInt ( "page" )
if page <= 1 {
page = 1
}
query := ctx . FormTrim ( "q" )
packageType := ctx . FormTrim ( "type" )
pvs , total , err := packages_model . SearchLatestVersions ( ctx , & packages_model . PackageSearchOptions {
Paginator : & db . ListOptions {
PageSize : setting . UI . PackagesPagingNum ,
Page : page ,
} ,
2022-07-27 03:59:10 +02:00
OwnerID : ctx . ContextUser . ID ,
Type : packages_model . Type ( packageType ) ,
Name : packages_model . SearchValue { Value : query } ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . Some ( false ) ,
2022-03-30 10:42:47 +02:00
} )
if err != nil {
ctx . ServerError ( "SearchLatestVersions" , err )
return
}
pds , err := packages_model . GetPackageDescriptors ( ctx , pvs )
if err != nil {
ctx . ServerError ( "GetPackageDescriptors" , err )
return
}
2022-05-07 18:21:15 +02:00
repositoryAccessMap := make ( map [ int64 ] bool )
for _ , pd := range pds {
if pd . Repository == nil {
continue
}
if _ , has := repositoryAccessMap [ pd . Repository . ID ] ; has {
continue
}
2022-05-11 18:09:36 +08:00
permission , err := access_model . GetUserRepoPermission ( ctx , pd . Repository , ctx . Doer )
2022-05-07 18:21:15 +02:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2024-04-20 11:15:04 +08:00
repositoryAccessMap [ pd . Repository . ID ] = permission . HasAnyUnitAccess ( )
2022-05-07 18:21:15 +02:00
}
2022-03-30 10:42:47 +02:00
hasPackages , err := packages_model . HasOwnerPackages ( ctx , ctx . ContextUser . ID )
if err != nil {
ctx . ServerError ( "HasOwnerPackages" , err )
return
}
2023-01-20 19:42:33 +08:00
shared_user . RenderUserHeader ( ctx )
2022-03-30 10:42:47 +02:00
ctx . Data [ "Title" ] = ctx . Tr ( "packages.title" )
ctx . Data [ "IsPackagesPage" ] = true
ctx . Data [ "Query" ] = query
ctx . Data [ "PackageType" ] = packageType
2022-12-29 00:31:54 +01:00
ctx . Data [ "AvailableTypes" ] = packages_model . TypeList
2022-03-30 10:42:47 +02:00
ctx . Data [ "HasPackages" ] = hasPackages
ctx . Data [ "PackageDescriptors" ] = pds
ctx . Data [ "Total" ] = total
2022-05-07 18:21:15 +02:00
ctx . Data [ "RepositoryAccessMap" ] = repositoryAccessMap
2022-03-30 10:42:47 +02:00
2023-08-11 19:08:05 +02:00
err = shared_user . LoadHeaderCount ( ctx )
if err != nil {
ctx . ServerError ( "LoadHeaderCount" , err )
return
}
2022-07-31 14:32:51 +02:00
// TODO: context/org -> HandleOrgAssignment() can not be used
if ctx . ContextUser . IsOrganization ( ) {
org := org_model . OrgFromUser ( ctx . ContextUser )
ctx . Data [ "Org" ] = org
ctx . Data [ "OrgLink" ] = ctx . ContextUser . OrganisationLink ( )
if ctx . Doer != nil {
ctx . Data [ "IsOrganizationMember" ] , _ = org_model . IsOrganizationMember ( ctx , org . ID , ctx . Doer . ID )
ctx . Data [ "IsOrganizationOwner" ] , _ = org_model . IsOrganizationOwner ( ctx , org . ID , ctx . Doer . ID )
} else {
ctx . Data [ "IsOrganizationMember" ] = false
ctx . Data [ "IsOrganizationOwner" ] = false
}
}
2022-03-30 10:42:47 +02:00
pager := context . NewPagination ( int ( total ) , setting . UI . PackagesPagingNum , page , 5 )
2024-03-16 20:07:56 +08:00
pager . AddParamString ( "q" , query )
pager . AddParamString ( "type" , packageType )
2022-03-30 10:42:47 +02:00
ctx . Data [ "Page" ] = pager
ctx . HTML ( http . StatusOK , tplPackagesList )
}
// RedirectToLastVersion redirects to the latest package version
func RedirectToLastVersion ( ctx * context . Context ) {
2024-06-19 06:32:45 +08:00
p , err := packages_model . GetPackageByName ( ctx , ctx . Package . Owner . ID , packages_model . Type ( ctx . PathParam ( "type" ) ) , ctx . PathParam ( "name" ) )
2022-03-30 10:42:47 +02:00
if err != nil {
if err == packages_model . ErrPackageNotExist {
ctx . NotFound ( "GetPackageByName" , err )
} else {
ctx . ServerError ( "GetPackageByName" , err )
}
return
}
pvs , _ , err := packages_model . SearchLatestVersions ( ctx , & packages_model . PackageSearchOptions {
2022-07-27 03:59:10 +02:00
PackageID : p . ID ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . Some ( false ) ,
2022-03-30 10:42:47 +02:00
} )
if err != nil {
ctx . ServerError ( "GetPackageByName" , err )
return
}
if len ( pvs ) == 0 {
ctx . NotFound ( "" , err )
return
}
pd , err := packages_model . GetPackageDescriptor ( ctx , pvs [ 0 ] )
if err != nil {
ctx . ServerError ( "GetPackageDescriptor" , err )
return
}
2024-03-03 01:38:38 +08:00
ctx . Redirect ( pd . VersionWebLink ( ) )
2022-03-30 10:42:47 +02:00
}
// ViewPackageVersion displays a single package version
func ViewPackageVersion ( ctx * context . Context ) {
pd := ctx . Package . Descriptor
2023-01-20 19:42:33 +08:00
shared_user . RenderUserHeader ( ctx )
2022-03-30 10:42:47 +02:00
ctx . Data [ "Title" ] = pd . Package . Name
ctx . Data [ "IsPackagesPage" ] = true
ctx . Data [ "PackageDescriptor" ] = pd
2024-12-05 00:09:07 +01:00
registryHostURL , err := url . Parse ( httplib . GuessCurrentHostURL ( ctx ) )
if err != nil {
registryHostURL , _ = url . Parse ( setting . AppURL )
}
ctx . Data [ "PackageRegistryHost" ] = registryHostURL . Host
2023-05-02 18:31:35 +02:00
switch pd . Package . Type {
2023-05-12 19:27:50 +02:00
case packages_model . TypeAlpine :
branches := make ( container . Set [ string ] )
repositories := make ( container . Set [ string ] )
architectures := make ( container . Set [ string ] )
for _ , f := range pd . Files {
for _ , pp := range f . Properties {
switch pp . Name {
case alpine_module . PropertyBranch :
branches . Add ( pp . Value )
case alpine_module . PropertyRepository :
repositories . Add ( pp . Value )
case alpine_module . PropertyArchitecture :
architectures . Add ( pp . Value )
}
}
}
2024-01-19 12:37:10 +01:00
ctx . Data [ "Branches" ] = util . Sorted ( branches . Values ( ) )
2024-12-05 00:09:07 +01:00
ctx . Data [ "Repositories" ] = util . Sorted ( repositories . Values ( ) )
ctx . Data [ "Architectures" ] = util . Sorted ( architectures . Values ( ) )
case packages_model . TypeArch :
repositories := make ( container . Set [ string ] )
architectures := make ( container . Set [ string ] )
for _ , f := range pd . Files {
for _ , pp := range f . Properties {
switch pp . Name {
case arch_module . PropertyRepository :
repositories . Add ( pp . Value )
case arch_module . PropertyArchitecture :
architectures . Add ( pp . Value )
}
}
}
2024-01-19 12:37:10 +01:00
ctx . Data [ "Repositories" ] = util . Sorted ( repositories . Values ( ) )
ctx . Data [ "Architectures" ] = util . Sorted ( architectures . Values ( ) )
2023-05-02 18:31:35 +02:00
case packages_model . TypeDebian :
distributions := make ( container . Set [ string ] )
components := make ( container . Set [ string ] )
architectures := make ( container . Set [ string ] )
for _ , f := range pd . Files {
for _ , pp := range f . Properties {
switch pp . Name {
case debian_module . PropertyDistribution :
distributions . Add ( pp . Value )
case debian_module . PropertyComponent :
components . Add ( pp . Value )
case debian_module . PropertyArchitecture :
architectures . Add ( pp . Value )
}
}
}
2024-01-19 12:37:10 +01:00
ctx . Data [ "Distributions" ] = util . Sorted ( distributions . Values ( ) )
ctx . Data [ "Components" ] = util . Sorted ( components . Values ( ) )
ctx . Data [ "Architectures" ] = util . Sorted ( architectures . Values ( ) )
case packages_model . TypeRpm :
groups := make ( container . Set [ string ] )
architectures := make ( container . Set [ string ] )
for _ , f := range pd . Files {
for _ , pp := range f . Properties {
switch pp . Name {
case rpm_module . PropertyGroup :
groups . Add ( pp . Value )
case rpm_module . PropertyArchitecture :
architectures . Add ( pp . Value )
}
}
}
ctx . Data [ "Groups" ] = util . Sorted ( groups . Values ( ) )
ctx . Data [ "Architectures" ] = util . Sorted ( architectures . Values ( ) )
2023-05-02 18:31:35 +02:00
}
2022-03-30 10:42:47 +02:00
var (
total int64
pvs [ ] * packages_model . PackageVersion
)
switch pd . Package . Type {
case packages_model . TypeContainer :
pvs , total , err = container_model . SearchImageTags ( ctx , & container_model . ImageTagsSearchOptions {
Paginator : db . NewAbsoluteListOptions ( 0 , 5 ) ,
PackageID : pd . Package . ID ,
IsTagged : true ,
} )
default :
pvs , total , err = packages_model . SearchVersions ( ctx , & packages_model . PackageSearchOptions {
2022-07-27 03:59:10 +02:00
Paginator : db . NewAbsoluteListOptions ( 0 , 5 ) ,
PackageID : pd . Package . ID ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . Some ( false ) ,
2022-03-30 10:42:47 +02:00
} )
}
if err != nil {
ctx . ServerError ( "" , err )
return
}
ctx . Data [ "LatestVersions" ] = pvs
ctx . Data [ "TotalVersionCount" ] = total
ctx . Data [ "CanWritePackages" ] = ctx . Package . AccessMode >= perm . AccessModeWrite || ctx . IsUserSiteAdmin ( )
2022-05-07 18:21:15 +02:00
hasRepositoryAccess := false
if pd . Repository != nil {
2022-05-11 18:09:36 +08:00
permission , err := access_model . GetUserRepoPermission ( ctx , pd . Repository , ctx . Doer )
2022-05-07 18:21:15 +02:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2024-04-20 11:15:04 +08:00
hasRepositoryAccess = permission . HasAnyUnitAccess ( )
2022-05-07 18:21:15 +02:00
}
ctx . Data [ "HasRepositoryAccess" ] = hasRepositoryAccess
2023-08-11 19:08:05 +02:00
err = shared_user . LoadHeaderCount ( ctx )
if err != nil {
ctx . ServerError ( "LoadHeaderCount" , err )
return
}
2022-03-30 10:42:47 +02:00
ctx . HTML ( http . StatusOK , tplPackagesView )
}
// ListPackageVersions lists all versions of a package
func ListPackageVersions ( ctx * context . Context ) {
2023-07-07 00:29:24 +05:30
shared_user . PrepareContextForProfileBigAvatar ( ctx )
2024-06-19 06:32:45 +08:00
p , err := packages_model . GetPackageByName ( ctx , ctx . Package . Owner . ID , packages_model . Type ( ctx . PathParam ( "type" ) ) , ctx . PathParam ( "name" ) )
2022-03-30 10:42:47 +02:00
if err != nil {
if err == packages_model . ErrPackageNotExist {
ctx . NotFound ( "GetPackageByName" , err )
} else {
ctx . ServerError ( "GetPackageByName" , err )
}
return
}
page := ctx . FormInt ( "page" )
if page <= 1 {
page = 1
}
pagination := & db . ListOptions {
PageSize : setting . UI . PackagesPagingNum ,
Page : page ,
}
query := ctx . FormTrim ( "q" )
2022-10-23 03:18:15 +02:00
sort := ctx . FormTrim ( "sort" )
2022-03-30 10:42:47 +02:00
2023-01-20 19:42:33 +08:00
shared_user . RenderUserHeader ( ctx )
2022-03-30 10:42:47 +02:00
ctx . Data [ "Title" ] = ctx . Tr ( "packages.title" )
ctx . Data [ "IsPackagesPage" ] = true
ctx . Data [ "PackageDescriptor" ] = & packages_model . PackageDescriptor {
Package : p ,
Owner : ctx . Package . Owner ,
}
ctx . Data [ "Query" ] = query
2022-10-23 03:18:15 +02:00
ctx . Data [ "Sort" ] = sort
2022-03-30 10:42:47 +02:00
pagerParams := map [ string ] string {
2022-10-23 03:18:15 +02:00
"q" : query ,
"sort" : sort ,
2022-03-30 10:42:47 +02:00
}
var (
total int64
pvs [ ] * packages_model . PackageVersion
)
switch p . Type {
case packages_model . TypeContainer :
tagged := ctx . FormTrim ( "tagged" )
pagerParams [ "tagged" ] = tagged
ctx . Data [ "Tagged" ] = tagged
pvs , total , err = container_model . SearchImageTags ( ctx , & container_model . ImageTagsSearchOptions {
Paginator : pagination ,
PackageID : p . ID ,
Query : query ,
IsTagged : tagged == "" || tagged == "tagged" ,
2022-10-23 03:18:15 +02:00
Sort : sort ,
2022-03-30 10:42:47 +02:00
} )
if err != nil {
ctx . ServerError ( "SearchImageTags" , err )
return
}
default :
pvs , total , err = packages_model . SearchVersions ( ctx , & packages_model . PackageSearchOptions {
2022-04-06 03:32:09 +02:00
Paginator : pagination ,
PackageID : p . ID ,
Version : packages_model . SearchValue {
ExactMatch : false ,
Value : query ,
} ,
2024-03-02 16:42:31 +01:00
IsInternal : optional . Some ( false ) ,
2022-10-23 03:18:15 +02:00
Sort : sort ,
2022-03-30 10:42:47 +02:00
} )
if err != nil {
ctx . ServerError ( "SearchVersions" , err )
return
}
}
ctx . Data [ "PackageDescriptors" ] , err = packages_model . GetPackageDescriptors ( ctx , pvs )
if err != nil {
ctx . ServerError ( "GetPackageDescriptors" , err )
return
}
ctx . Data [ "Total" ] = total
2023-08-11 19:08:05 +02:00
err = shared_user . LoadHeaderCount ( ctx )
if err != nil {
ctx . ServerError ( "LoadHeaderCount" , err )
return
}
2022-03-30 10:42:47 +02:00
pager := context . NewPagination ( int ( total ) , setting . UI . PackagesPagingNum , page , 5 )
for k , v := range pagerParams {
pager . AddParamString ( k , v )
}
ctx . Data [ "Page" ] = pager
ctx . HTML ( http . StatusOK , tplPackageVersionList )
}
// PackageSettings displays the package settings page
func PackageSettings ( ctx * context . Context ) {
pd := ctx . Package . Descriptor
2023-01-20 19:42:33 +08:00
shared_user . RenderUserHeader ( ctx )
2022-03-30 10:42:47 +02:00
ctx . Data [ "Title" ] = pd . Package . Name
ctx . Data [ "IsPackagesPage" ] = true
ctx . Data [ "PackageDescriptor" ] = pd
2023-10-15 23:46:06 +08:00
repos , _ , _ := repo_model . GetUserRepositories ( ctx , & repo_model . SearchRepoOptions {
2022-04-09 17:57:37 +02:00
Actor : pd . Owner ,
Private : true ,
2022-03-30 10:42:47 +02:00
} )
ctx . Data [ "Repos" ] = repos
ctx . Data [ "CanWritePackages" ] = ctx . Package . AccessMode >= perm . AccessModeWrite || ctx . IsUserSiteAdmin ( )
2023-09-06 10:49:36 +02:00
err := shared_user . LoadHeaderCount ( ctx )
if err != nil {
ctx . ServerError ( "LoadHeaderCount" , err )
return
}
2022-03-30 10:42:47 +02:00
ctx . HTML ( http . StatusOK , tplPackagesSettings )
}
// PackageSettingsPost updates the package settings
func PackageSettingsPost ( ctx * context . Context ) {
pd := ctx . Package . Descriptor
form := web . GetForm ( ctx ) . ( * forms . PackageSettingForm )
switch form . Action {
case "link" :
success := func ( ) bool {
repoID := int64 ( 0 )
if form . RepoID != 0 {
2022-12-03 10:48:26 +08:00
repo , err := repo_model . GetRepositoryByID ( ctx , form . RepoID )
2022-03-30 10:42:47 +02:00
if err != nil {
log . Error ( "Error getting repository: %v" , err )
return false
}
if repo . OwnerID != pd . Owner . ID {
return false
}
repoID = repo . ID
}
if err := packages_model . SetRepositoryLink ( ctx , pd . Package . ID , repoID ) ; err != nil {
log . Error ( "Error updating package: %v" , err )
return false
}
return true
} ( )
if success {
ctx . Flash . Success ( ctx . Tr ( "packages.settings.link.success" ) )
} else {
ctx . Flash . Error ( ctx . Tr ( "packages.settings.link.error" ) )
}
ctx . Redirect ( ctx . Link )
return
case "delete" :
2023-09-25 15:17:37 +02:00
err := packages_service . RemovePackageVersion ( ctx , ctx . Doer , ctx . Package . Descriptor . Version )
2022-03-30 10:42:47 +02:00
if err != nil {
log . Error ( "Error deleting package: %v" , err )
ctx . Flash . Error ( ctx . Tr ( "packages.settings.delete.error" ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "packages.settings.delete.success" ) )
}
2023-06-29 19:01:14 +02:00
redirectURL := ctx . Package . Owner . HomeLink ( ) + "/-/packages"
// redirect to the package if there are still versions available
2024-03-02 16:42:31 +01:00
if has , _ := packages_model . ExistVersion ( ctx , & packages_model . PackageSearchOptions { PackageID : ctx . Package . Descriptor . Package . ID , IsInternal : optional . Some ( false ) } ) ; has {
2023-06-29 19:01:14 +02:00
redirectURL = ctx . Package . Descriptor . PackageWebLink ( )
}
ctx . Redirect ( redirectURL )
2022-03-30 10:42:47 +02:00
return
}
}
// DownloadPackageFile serves the content of a package file
func DownloadPackageFile ( ctx * context . Context ) {
2024-06-19 06:32:45 +08:00
pf , err := packages_model . GetFileForVersionByID ( ctx , ctx . Package . Descriptor . Version . ID , ctx . PathParamInt64 ( ":fileid" ) )
2022-03-30 10:42:47 +02:00
if err != nil {
if err == packages_model . ErrPackageFileNotExist {
ctx . NotFound ( "" , err )
} else {
ctx . ServerError ( "GetFileForVersionByID" , err )
}
return
}
2023-07-03 15:33:28 +02:00
s , u , _ , err := packages_service . GetPackageFileStream ( ctx , pf )
2022-03-30 10:42:47 +02:00
if err != nil {
ctx . ServerError ( "GetPackageFileStream" , err )
return
}
2023-07-03 15:33:28 +02:00
packages_helper . ServePackageFile ( ctx , s , u , pf )
2022-03-30 10:42:47 +02:00
}