gitea源码

package_file.go 7.0KB

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