初始提交: Gitea 项目代码
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(PackageFile))
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrDuplicatePackageFile indicates a duplicated package file error
|
||||
ErrDuplicatePackageFile = util.NewAlreadyExistErrorf("package file already exists")
|
||||
// ErrPackageFileNotExist indicates a package file not exist error
|
||||
ErrPackageFileNotExist = util.NewNotExistErrorf("package file does not exist")
|
||||
)
|
||||
|
||||
// EmptyFileKey is a named constant for an empty file key
|
||||
const EmptyFileKey = ""
|
||||
|
||||
// PackageFile represents a package file
|
||||
type PackageFile struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
VersionID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
BlobID int64 `xorm:"INDEX NOT NULL"`
|
||||
Name string `xorm:"NOT NULL"`
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
CompositeKey string `xorm:"UNIQUE(s) INDEX"`
|
||||
IsLead bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
|
||||
}
|
||||
|
||||
// TryInsertFile inserts a file. If the file exists already ErrDuplicatePackageFile is returned
|
||||
func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
existing := &PackageFile{}
|
||||
|
||||
has, err := e.Where(builder.Eq{
|
||||
"version_id": pf.VersionID,
|
||||
"lower_name": pf.LowerName,
|
||||
"composite_key": pf.CompositeKey,
|
||||
}).Get(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return existing, ErrDuplicatePackageFile
|
||||
}
|
||||
if _, err = e.Insert(pf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pf, nil
|
||||
}
|
||||
|
||||
// GetFilesByVersionID gets all files of a version
|
||||
func GetFilesByVersionID(ctx context.Context, versionID int64) ([]*PackageFile, error) {
|
||||
pfs := make([]*PackageFile, 0, 10)
|
||||
return pfs, db.GetEngine(ctx).Where("version_id = ?", versionID).OrderBy("lower_name, created_unix, id").Find(&pfs)
|
||||
}
|
||||
|
||||
// GetFileForVersionByID gets a file of a version by id
|
||||
func GetFileForVersionByID(ctx context.Context, versionID, fileID int64) (*PackageFile, error) {
|
||||
pf := &PackageFile{
|
||||
VersionID: versionID,
|
||||
}
|
||||
|
||||
has, err := db.GetEngine(ctx).ID(fileID).Get(pf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, ErrPackageFileNotExist
|
||||
}
|
||||
return pf, nil
|
||||
}
|
||||
|
||||
// GetFileForVersionByName gets a file of a version by name
|
||||
func GetFileForVersionByName(ctx context.Context, versionID int64, name, key string) (*PackageFile, error) {
|
||||
if name == "" {
|
||||
return nil, ErrPackageFileNotExist
|
||||
}
|
||||
|
||||
pf := &PackageFile{}
|
||||
|
||||
has, err := db.GetEngine(ctx).Where(builder.Eq{
|
||||
"version_id": versionID,
|
||||
"lower_name": strings.ToLower(name),
|
||||
"composite_key": key,
|
||||
}).Get(pf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, ErrPackageFileNotExist
|
||||
}
|
||||
return pf, nil
|
||||
}
|
||||
|
||||
// DeleteFileByID deletes a file
|
||||
func DeleteFileByID(ctx context.Context, fileID int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(fileID).Delete(&PackageFile{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFilesByPackageID deletes all files of a specific package
|
||||
// Versions must not be deleted prior to this call
|
||||
func DeleteFilesByPackageID(ctx context.Context, packageID int64) error {
|
||||
deleteStmt := builder.Delete(builder.In("version_id", builder.Select("package_version.id").From("package_version").Where(builder.Eq{"package_id": packageID}))).From("package_file")
|
||||
_, err := db.GetEngine(ctx).Exec(deleteStmt)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFilesByVersionID deletes all files of a specific version
|
||||
func DeleteFilesByVersionID(ctx context.Context, versionID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("version_id = ?", versionID).Delete(&PackageFile{})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error {
|
||||
_, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf)
|
||||
return err
|
||||
}
|
||||
|
||||
// PackageFileSearchOptions are options for SearchXXX methods
|
||||
type PackageFileSearchOptions struct {
|
||||
OwnerID int64
|
||||
PackageType Type
|
||||
VersionID int64
|
||||
Query string
|
||||
CompositeKey string
|
||||
Properties map[string]string
|
||||
OlderThan time.Duration
|
||||
HashAlgorithm string
|
||||
Hash string
|
||||
db.Paginator
|
||||
}
|
||||
|
||||
func (opts *PackageFileSearchOptions) toConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if opts.VersionID != 0 {
|
||||
cond = cond.And(builder.Eq{"package_file.version_id": opts.VersionID})
|
||||
} else if opts.OwnerID != 0 || (opts.PackageType != "" && opts.PackageType != "all") {
|
||||
var versionCond builder.Cond = builder.Eq{
|
||||
"package_version.is_internal": false,
|
||||
}
|
||||
if opts.OwnerID != 0 {
|
||||
versionCond = versionCond.And(builder.Eq{"package.owner_id": opts.OwnerID})
|
||||
}
|
||||
if opts.PackageType != "" && opts.PackageType != "all" {
|
||||
versionCond = versionCond.And(builder.Eq{"package.type": opts.PackageType})
|
||||
}
|
||||
|
||||
in := builder.
|
||||
Select("package_version.id").
|
||||
From("package_version").
|
||||
InnerJoin("package", "package.id = package_version.package_id").
|
||||
Where(versionCond)
|
||||
|
||||
cond = cond.And(builder.In("package_file.version_id", in))
|
||||
}
|
||||
if opts.CompositeKey != "" {
|
||||
cond = cond.And(builder.Eq{"package_file.composite_key": opts.CompositeKey})
|
||||
}
|
||||
if opts.Query != "" {
|
||||
cond = cond.And(builder.Like{"package_file.lower_name", strings.ToLower(opts.Query)})
|
||||
}
|
||||
|
||||
if len(opts.Properties) != 0 {
|
||||
var propsCond builder.Cond = builder.Eq{
|
||||
"package_property.ref_type": PropertyTypeFile,
|
||||
}
|
||||
propsCond = propsCond.And(builder.Expr("package_property.ref_id = package_file.id"))
|
||||
|
||||
propsCondBlock := builder.NewCond()
|
||||
for name, value := range opts.Properties {
|
||||
propsCondBlock = propsCondBlock.Or(builder.Eq{
|
||||
"package_property.name": name,
|
||||
"package_property.value": value,
|
||||
})
|
||||
}
|
||||
propsCond = propsCond.And(propsCondBlock)
|
||||
|
||||
cond = cond.And(builder.Eq{
|
||||
strconv.Itoa(len(opts.Properties)): builder.Select("COUNT(*)").Where(propsCond).From("package_property"),
|
||||
})
|
||||
}
|
||||
|
||||
if opts.OlderThan != 0 {
|
||||
cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-opts.OlderThan).Unix()})
|
||||
}
|
||||
|
||||
if opts.Hash != "" {
|
||||
var field string
|
||||
switch strings.ToLower(opts.HashAlgorithm) {
|
||||
case "md5":
|
||||
field = "package_blob.hash_md5"
|
||||
case "sha1":
|
||||
field = "package_blob.hash_sha1"
|
||||
case "sha256":
|
||||
field = "package_blob.hash_sha256"
|
||||
case "sha512":
|
||||
fallthrough
|
||||
default: // default to SHA512 if not specified or unknown
|
||||
field = "package_blob.hash_sha512"
|
||||
}
|
||||
innerCond := builder.
|
||||
Expr("package_blob.id = package_file.blob_id").
|
||||
And(builder.Eq{field: opts.Hash})
|
||||
cond = cond.And(builder.Exists(builder.Select("package_blob.id").From("package_blob").Where(innerCond)))
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
// SearchFiles gets all files of packages matching the search options
|
||||
func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*PackageFile, int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Where(opts.toConds())
|
||||
|
||||
if opts.Paginator != nil {
|
||||
db.SetSessionPagination(sess, opts)
|
||||
}
|
||||
|
||||
pfs := make([]*PackageFile, 0, 10)
|
||||
count, err := sess.FindAndCount(&pfs)
|
||||
return pfs, count, err
|
||||
}
|
||||
|
||||
// HasFiles tests if there are files of packages matching the search options
|
||||
func HasFiles(ctx context.Context, opts *PackageFileSearchOptions) (bool, error) {
|
||||
return db.Exist[PackageFile](ctx, opts.toConds())
|
||||
}
|
||||
|
||||
// CalculateFileSize sums up all blob sizes matching the search options.
|
||||
// It does NOT respect the deduplication of blobs.
|
||||
func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Table("package_file").
|
||||
Where(opts.toConds()).
|
||||
Join("INNER", "package_blob", "package_blob.id = package_file.blob_id").
|
||||
SumInt(new(PackageBlob), "size")
|
||||
}
|
||||
Reference in New Issue
Block a user