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_file.go 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package packages
  4. import (
  5. "context"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/modules/timeutil"
  11. "code.gitea.io/gitea/modules/util"
  12. "xorm.io/builder"
  13. )
  14. func init() {
  15. db.RegisterModel(new(PackageFile))
  16. }
  17. var (
  18. // ErrDuplicatePackageFile indicates a duplicated package file error
  19. ErrDuplicatePackageFile = util.NewAlreadyExistErrorf("package file already exists")
  20. // ErrPackageFileNotExist indicates a package file not exist error
  21. ErrPackageFileNotExist = util.NewNotExistErrorf("package file does not exist")
  22. )
  23. // EmptyFileKey is a named constant for an empty file key
  24. const EmptyFileKey = ""
  25. // PackageFile represents a package file
  26. type PackageFile struct {
  27. ID int64 `xorm:"pk autoincr"`
  28. VersionID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
  29. BlobID int64 `xorm:"INDEX NOT NULL"`
  30. Name string `xorm:"NOT NULL"`
  31. LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
  32. CompositeKey string `xorm:"UNIQUE(s) INDEX"`
  33. IsLead bool `xorm:"NOT NULL DEFAULT false"`
  34. CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
  35. }
  36. // TryInsertFile inserts a file. If the file exists already ErrDuplicatePackageFile is returned
  37. func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) {
  38. e := db.GetEngine(ctx)
  39. key := &PackageFile{
  40. VersionID: pf.VersionID,
  41. LowerName: pf.LowerName,
  42. CompositeKey: pf.CompositeKey,
  43. }
  44. has, err := e.Get(key)
  45. if err != nil {
  46. return nil, err
  47. }
  48. if has {
  49. return pf, ErrDuplicatePackageFile
  50. }
  51. if _, err = e.Insert(pf); err != nil {
  52. return nil, err
  53. }
  54. return pf, nil
  55. }
  56. // GetFilesByVersionID gets all files of a version
  57. func GetFilesByVersionID(ctx context.Context, versionID int64) ([]*PackageFile, error) {
  58. pfs := make([]*PackageFile, 0, 10)
  59. return pfs, db.GetEngine(ctx).Where("version_id = ?", versionID).Find(&pfs)
  60. }
  61. // GetFileForVersionByID gets a file of a version by id
  62. func GetFileForVersionByID(ctx context.Context, versionID, fileID int64) (*PackageFile, error) {
  63. pf := &PackageFile{
  64. VersionID: versionID,
  65. }
  66. has, err := db.GetEngine(ctx).ID(fileID).Get(pf)
  67. if err != nil {
  68. return nil, err
  69. }
  70. if !has {
  71. return nil, ErrPackageFileNotExist
  72. }
  73. return pf, nil
  74. }
  75. // GetFileForVersionByName gets a file of a version by name
  76. func GetFileForVersionByName(ctx context.Context, versionID int64, name, key string) (*PackageFile, error) {
  77. if name == "" {
  78. return nil, ErrPackageFileNotExist
  79. }
  80. pf := &PackageFile{
  81. VersionID: versionID,
  82. LowerName: strings.ToLower(name),
  83. CompositeKey: key,
  84. }
  85. has, err := db.GetEngine(ctx).Get(pf)
  86. if err != nil {
  87. return nil, err
  88. }
  89. if !has {
  90. return nil, ErrPackageFileNotExist
  91. }
  92. return pf, nil
  93. }
  94. // DeleteFileByID deletes a file
  95. func DeleteFileByID(ctx context.Context, fileID int64) error {
  96. _, err := db.GetEngine(ctx).ID(fileID).Delete(&PackageFile{})
  97. return err
  98. }
  99. // PackageFileSearchOptions are options for SearchXXX methods
  100. type PackageFileSearchOptions struct {
  101. OwnerID int64
  102. PackageType string
  103. VersionID int64
  104. Query string
  105. CompositeKey string
  106. Properties map[string]string
  107. OlderThan time.Duration
  108. db.Paginator
  109. }
  110. func (opts *PackageFileSearchOptions) toConds() builder.Cond {
  111. cond := builder.NewCond()
  112. if opts.VersionID != 0 {
  113. cond = cond.And(builder.Eq{"package_file.version_id": opts.VersionID})
  114. } else if opts.OwnerID != 0 || (opts.PackageType != "" && opts.PackageType != "all") {
  115. var versionCond builder.Cond = builder.Eq{
  116. "package_version.is_internal": false,
  117. }
  118. if opts.OwnerID != 0 {
  119. versionCond = versionCond.And(builder.Eq{"package.owner_id": opts.OwnerID})
  120. }
  121. if opts.PackageType != "" && opts.PackageType != "all" {
  122. versionCond = versionCond.And(builder.Eq{"package.type": opts.PackageType})
  123. }
  124. in := builder.
  125. Select("package_version.id").
  126. From("package_version").
  127. InnerJoin("package", "package.id = package_version.package_id").
  128. Where(versionCond)
  129. cond = cond.And(builder.In("package_file.version_id", in))
  130. }
  131. if opts.CompositeKey != "" {
  132. cond = cond.And(builder.Eq{"package_file.composite_key": opts.CompositeKey})
  133. }
  134. if opts.Query != "" {
  135. cond = cond.And(builder.Like{"package_file.lower_name", strings.ToLower(opts.Query)})
  136. }
  137. if len(opts.Properties) != 0 {
  138. var propsCond builder.Cond = builder.Eq{
  139. "package_property.ref_type": PropertyTypeFile,
  140. }
  141. propsCond = propsCond.And(builder.Expr("package_property.ref_id = package_file.id"))
  142. propsCondBlock := builder.NewCond()
  143. for name, value := range opts.Properties {
  144. propsCondBlock = propsCondBlock.Or(builder.Eq{
  145. "package_property.name": name,
  146. "package_property.value": value,
  147. })
  148. }
  149. propsCond = propsCond.And(propsCondBlock)
  150. cond = cond.And(builder.Eq{
  151. strconv.Itoa(len(opts.Properties)): builder.Select("COUNT(*)").Where(propsCond).From("package_property"),
  152. })
  153. }
  154. if opts.OlderThan != 0 {
  155. cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-opts.OlderThan).Unix()})
  156. }
  157. return cond
  158. }
  159. // SearchFiles gets all files of packages matching the search options
  160. func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*PackageFile, int64, error) {
  161. sess := db.GetEngine(ctx).
  162. Where(opts.toConds())
  163. if opts.Paginator != nil {
  164. sess = db.SetSessionPagination(sess, opts)
  165. }
  166. pfs := make([]*PackageFile, 0, 10)
  167. count, err := sess.FindAndCount(&pfs)
  168. return pfs, count, err
  169. }
  170. // CalculateFileSize sums up all blob sizes matching the search options.
  171. // It does NOT respect the deduplication of blobs.
  172. func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) {
  173. return db.GetEngine(ctx).
  174. Table("package_file").
  175. Where(opts.toConds()).
  176. Join("INNER", "package_blob", "package_blob.id = package_file.blob_id").
  177. SumInt(new(PackageBlob), "size")
  178. }