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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 Type
  103. VersionID int64
  104. Query string
  105. CompositeKey string
  106. Properties map[string]string
  107. OlderThan time.Duration
  108. HashAlgorithm string
  109. Hash string
  110. db.Paginator
  111. }
  112. func (opts *PackageFileSearchOptions) toConds() builder.Cond {
  113. cond := builder.NewCond()
  114. if opts.VersionID != 0 {
  115. cond = cond.And(builder.Eq{"package_file.version_id": opts.VersionID})
  116. } else if opts.OwnerID != 0 || (opts.PackageType != "" && opts.PackageType != "all") {
  117. var versionCond builder.Cond = builder.Eq{
  118. "package_version.is_internal": false,
  119. }
  120. if opts.OwnerID != 0 {
  121. versionCond = versionCond.And(builder.Eq{"package.owner_id": opts.OwnerID})
  122. }
  123. if opts.PackageType != "" && opts.PackageType != "all" {
  124. versionCond = versionCond.And(builder.Eq{"package.type": opts.PackageType})
  125. }
  126. in := builder.
  127. Select("package_version.id").
  128. From("package_version").
  129. InnerJoin("package", "package.id = package_version.package_id").
  130. Where(versionCond)
  131. cond = cond.And(builder.In("package_file.version_id", in))
  132. }
  133. if opts.CompositeKey != "" {
  134. cond = cond.And(builder.Eq{"package_file.composite_key": opts.CompositeKey})
  135. }
  136. if opts.Query != "" {
  137. cond = cond.And(builder.Like{"package_file.lower_name", strings.ToLower(opts.Query)})
  138. }
  139. if len(opts.Properties) != 0 {
  140. var propsCond builder.Cond = builder.Eq{
  141. "package_property.ref_type": PropertyTypeFile,
  142. }
  143. propsCond = propsCond.And(builder.Expr("package_property.ref_id = package_file.id"))
  144. propsCondBlock := builder.NewCond()
  145. for name, value := range opts.Properties {
  146. propsCondBlock = propsCondBlock.Or(builder.Eq{
  147. "package_property.name": name,
  148. "package_property.value": value,
  149. })
  150. }
  151. propsCond = propsCond.And(propsCondBlock)
  152. cond = cond.And(builder.Eq{
  153. strconv.Itoa(len(opts.Properties)): builder.Select("COUNT(*)").Where(propsCond).From("package_property"),
  154. })
  155. }
  156. if opts.OlderThan != 0 {
  157. cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-opts.OlderThan).Unix()})
  158. }
  159. if opts.Hash != "" {
  160. var field string
  161. switch strings.ToLower(opts.HashAlgorithm) {
  162. case "md5":
  163. field = "package_blob.hash_md5"
  164. case "sha1":
  165. field = "package_blob.hash_sha1"
  166. case "sha256":
  167. field = "package_blob.hash_sha256"
  168. case "sha512":
  169. fallthrough
  170. default: // default to SHA512 if not specified or unknown
  171. field = "package_blob.hash_sha512"
  172. }
  173. innerCond := builder.
  174. Expr("package_blob.id = package_file.blob_id").
  175. And(builder.Eq{field: opts.Hash})
  176. cond = cond.And(builder.Exists(builder.Select("package_blob.id").From("package_blob").Where(innerCond)))
  177. }
  178. return cond
  179. }
  180. // SearchFiles gets all files of packages matching the search options
  181. func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*PackageFile, int64, error) {
  182. sess := db.GetEngine(ctx).
  183. Where(opts.toConds())
  184. if opts.Paginator != nil {
  185. sess = db.SetSessionPagination(sess, opts)
  186. }
  187. pfs := make([]*PackageFile, 0, 10)
  188. count, err := sess.FindAndCount(&pfs)
  189. return pfs, count, err
  190. }
  191. // CalculateFileSize sums up all blob sizes matching the search options.
  192. // It does NOT respect the deduplication of blobs.
  193. func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) {
  194. return db.GetEngine(ctx).
  195. Table("package_file").
  196. Where(opts.toConds()).
  197. Join("INNER", "package_blob", "package_blob.id = package_file.blob_id").
  198. SumInt(new(PackageBlob), "size")
  199. }