366 lines
8.6 KiB
Go
366 lines
8.6 KiB
Go
package manifests
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/samber/lo"
|
|
"github.com/sirupsen/logrus"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
"io"
|
|
"net/http"
|
|
"nf-repo/internal/interfaces"
|
|
"nf-repo/internal/model"
|
|
"nf-repo/internal/util/rerr"
|
|
"nf-repo/internal/util/tools"
|
|
)
|
|
|
|
type pm struct {
|
|
model.PackageManifest
|
|
Digest string `json:"digest" gorm:"column:digest"`
|
|
Size int `json:"size" gorm:"column:size"`
|
|
}
|
|
|
|
type dbManifests struct {
|
|
db interfaces.Database
|
|
}
|
|
|
|
func (m *dbManifests) Get(ctx context.Context, repo string, target string) (io.ReadCloser, string, *rerr.RepositoryError) {
|
|
var (
|
|
err error
|
|
hash model.Hash
|
|
pd = new(model.PackageDigest)
|
|
)
|
|
|
|
if hash, err = model.NewHash(target); err == nil {
|
|
if err = m.db.TX(tools.Timeout(5)).
|
|
Model(&model.PackageDigest{}).
|
|
Where("digest", hash.String()).
|
|
Take(pd).
|
|
Error; err != nil {
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, "", &rerr.RepositoryError{
|
|
Status: http.StatusNotFound,
|
|
Code: "NAME_UNKNOWN",
|
|
Message: fmt.Sprintf("Unknown name: %s@%s", repo, target),
|
|
}
|
|
}
|
|
|
|
return nil, "", rerr.ErrInternal(err)
|
|
}
|
|
|
|
return io.NopCloser(bytes.NewReader(pd.Content)), pd.ContentType, nil
|
|
}
|
|
|
|
var pm = new(model.PackageManifest)
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).
|
|
Model(&model.PackageManifest{}).
|
|
Where("repo", repo).
|
|
Where("tag", target).
|
|
Take(pm).
|
|
Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, "", &rerr.RepositoryError{
|
|
Status: http.StatusNotFound,
|
|
Code: "NAME_UNKNOWN",
|
|
Message: fmt.Sprintf("Unknown name: %s@%s", repo, target),
|
|
}
|
|
}
|
|
|
|
return nil, "", rerr.ErrInternal(err)
|
|
}
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).
|
|
Model(&model.PackageDigest{}).
|
|
Where("id", pm.DigestId).
|
|
Take(pd).
|
|
Error; err != nil {
|
|
logrus.
|
|
WithField("path", "dbManifests.Get").
|
|
WithField("digest", pm.DigestId).
|
|
WithField("err", err.Error()).
|
|
Error()
|
|
return nil, "", rerr.ErrInternal(err)
|
|
}
|
|
|
|
return io.NopCloser(bytes.NewReader(pd.Content)), pd.ContentType, nil
|
|
}
|
|
|
|
func (m *dbManifests) Put(ctx context.Context, repo string, tag string, digest string, mf *model.RepoSimpleManifest) *rerr.RepositoryError {
|
|
|
|
var (
|
|
err error
|
|
pm = &model.PackageManifest{
|
|
Repo: repo,
|
|
Tag: tag,
|
|
}
|
|
|
|
pd = &model.PackageDigest{
|
|
Digest: digest,
|
|
ContentType: mf.ContentType,
|
|
Content: mf.Blob,
|
|
}
|
|
|
|
blob = new(model.RepoSimpleManifestBlob)
|
|
)
|
|
|
|
if err = json.Unmarshal(mf.Blob, blob); err != nil {
|
|
return rerr.ErrInternal(err)
|
|
}
|
|
|
|
pd.Size = blob.CountSize()
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).
|
|
Clauses(clause.Returning{}).
|
|
Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "digest"}},
|
|
DoUpdates: clause.Set{
|
|
{
|
|
Column: clause.Column{Name: "content_type"},
|
|
Value: mf.ContentType,
|
|
},
|
|
{
|
|
Column: clause.Column{Name: "content"},
|
|
Value: mf.Blob,
|
|
},
|
|
{
|
|
Column: clause.Column{Name: "size"},
|
|
Value: pd.Size,
|
|
},
|
|
}}).
|
|
Create(pd).
|
|
Error; err != nil {
|
|
logrus.
|
|
WithField("path", "dbManifests.Put.Create").
|
|
WithField("err", err.Error()).
|
|
Error()
|
|
return rerr.ErrInternal(err)
|
|
}
|
|
|
|
pm.DigestId = pd.Id
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).Create(pm).Error; err == nil {
|
|
return nil
|
|
}
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).Model(&model.PackageManifest{}).
|
|
Where("(repo = ? AND tag = ?)", repo, tag).
|
|
Updates(map[string]any{
|
|
"repo": repo,
|
|
"tag": tag,
|
|
"digest_id": pm.DigestId,
|
|
}).
|
|
Error; err != nil {
|
|
logrus.
|
|
WithField("path", "dbManifests.Put.Updates").
|
|
WithField("err", err.Error()).
|
|
Debug()
|
|
return rerr.ErrInternal(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *dbManifests) Delete(ctx context.Context, repo string, target string) *rerr.RepositoryError {
|
|
var (
|
|
err error
|
|
)
|
|
|
|
if err = m.db.TX(tools.Timeout(5)).
|
|
Where("repo", repo).
|
|
Where("target", target).
|
|
Delete(&model.PackageManifest{}).
|
|
Error; err != nil {
|
|
return rerr.ErrInternal(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *dbManifests) Catalog(ctx context.Context, limit int, last int, keyword string) (*model.Catalog, *rerr.RepositoryError) {
|
|
var (
|
|
err error
|
|
list = make([]*pm, 0)
|
|
tx = m.db.TX(tools.Timeout(5))
|
|
)
|
|
|
|
tx = tx.Model(&model.PackageManifest{}).
|
|
Select("\"package_manifests\".*", "\"pd\".\"digest\" as digest", "\"pd\".\"size\" as size")
|
|
if keyword != "" {
|
|
k := fmt.Sprintf("%%%s%%", keyword)
|
|
tx = tx.Where("package_manifests.repo like ?", k)
|
|
}
|
|
|
|
if err = tx.Group("\"package_manifests\".\"repo\"").
|
|
Joins("LEFT JOIN package_digests pd on \"pd\".\"id\" = \"package_manifests\".\"digest_id\"").
|
|
Order("updated_at DESC, tag = 'latest' DESC").
|
|
Offset(last).
|
|
Limit(limit).
|
|
Find(&list).
|
|
Error; err != nil {
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
|
|
return &model.Catalog{
|
|
Repositories: lo.Map(list, func(item *pm, index int) string {
|
|
return item.Repo
|
|
}),
|
|
Repos: lo.Map(list, func(item *pm, index int) *model.PackageManifest {
|
|
item.PackageManifest.Digest = item.Digest
|
|
item.PackageManifest.Size = item.Size
|
|
return &item.PackageManifest
|
|
}),
|
|
}, nil
|
|
}
|
|
|
|
func (m *dbManifests) Tags(ctx context.Context, repo string, limit, last int, keyword string) (*model.Tag, *rerr.RepositoryError) {
|
|
var (
|
|
err error
|
|
list = make([]*pm, 0)
|
|
tx = m.db.TX(tools.Timeout(5)).Model(&model.PackageManifest{}).Select("\"package_manifests\".*", "\"pd\".\"digest\" as digest")
|
|
txc = m.db.TX(tools.Timeout(5)).Model(&model.PackageManifest{}).Select("COUNT(id)")
|
|
total int
|
|
)
|
|
|
|
if keyword != "" {
|
|
k := fmt.Sprintf("%%%s%%", keyword)
|
|
tx = tx.Where("\"package_manifests\".\"tag\" like ?", k)
|
|
txc = txc.Where("tag like ?", k)
|
|
}
|
|
|
|
if err = txc.Find(&total).Error; err != nil {
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
|
|
if err = tx.
|
|
Where("package_manifests.repo", repo).
|
|
Joins("LEFT JOIN package_digests pd on \"pd\".\"id\" = \"package_manifests\".\"digest_id\"").
|
|
Order("updated_at").
|
|
Offset(last).
|
|
Limit(limit).
|
|
Find(&list).
|
|
Error; err != nil {
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
|
|
return &model.Tag{
|
|
Name: repo,
|
|
Tags: lo.Map(list, func(item *pm, index int) string {
|
|
return item.Tag
|
|
}),
|
|
RepoTags: lo.Map(list, func(item *pm, index int) *model.PackageManifest {
|
|
item.PackageManifest.Digest = item.Digest
|
|
return &item.PackageManifest
|
|
}),
|
|
Total: total,
|
|
}, nil
|
|
}
|
|
|
|
func (m *dbManifests) Referrers(ctx context.Context, repo string, target string) (*model.IndexManifest, *rerr.RepositoryError) {
|
|
var (
|
|
err error
|
|
pm = new(model.PackageManifest)
|
|
pd = new(model.PackageDigest)
|
|
manifest = &model.IndexManifest{}
|
|
tx = m.db.TX(tools.Timeout(5))
|
|
hash model.Hash
|
|
)
|
|
|
|
if hash, err = model.NewHash(target); err == nil {
|
|
if err = tx.Model(&model.PackageDigest{}).
|
|
Where("repo", repo).
|
|
Where("digest", hash.String()).
|
|
Take(pd).
|
|
Error; err != nil {
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, &rerr.RepositoryError{
|
|
Status: http.StatusNotFound,
|
|
Code: "NAME_UNKNOWN",
|
|
Message: fmt.Sprintf("Unknown name: %s@%s", repo, target),
|
|
}
|
|
}
|
|
|
|
logrus.
|
|
WithField("path", "dbManifests.Referrers.Take").
|
|
WithField("repo", repo).
|
|
WithField("target", target).
|
|
WithField("err", err.Error()).
|
|
Debug()
|
|
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
} else {
|
|
if err = tx.Model(&model.PackageManifest{}).
|
|
Where("repo", repo).
|
|
Where("tag", target).
|
|
Take(pd).
|
|
Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, &rerr.RepositoryError{
|
|
Status: http.StatusNotFound,
|
|
Code: "NAME_UNKNOWN",
|
|
Message: fmt.Sprintf("Unknown name: %s@%s", repo, target),
|
|
}
|
|
}
|
|
|
|
logrus.
|
|
WithField("path", "dbManifests.Referrers.Take").
|
|
WithField("repo", repo).
|
|
WithField("target", target).
|
|
Error()
|
|
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
|
|
if err = tx.Model(&model.PackageDigest{}).
|
|
Where("digest_id", pm.DigestId).
|
|
Take(pd).
|
|
Error; err != nil {
|
|
|
|
logrus.
|
|
WithField("path", "dbManifests.Referrers.Take").
|
|
WithField("repo", repo).
|
|
WithField("target", target).
|
|
Error()
|
|
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
}
|
|
|
|
if err = json.Unmarshal(pd.Content, manifest); err != nil {
|
|
logrus.
|
|
WithField("path", "dbManifests.Referrers.Unmarshal").
|
|
WithField("repo", repo).
|
|
WithField("target", target).
|
|
WithField("err", err.Error()).
|
|
Debug()
|
|
return nil, rerr.ErrInternal(err)
|
|
}
|
|
|
|
return manifest, nil
|
|
}
|
|
|
|
func NewManifestDBHandler(tx interfaces.Database) interfaces.ManifestHandler {
|
|
var (
|
|
err error
|
|
)
|
|
|
|
if err = tx.TX(tools.Timeout(5)).AutoMigrate(
|
|
&model.PackageManifest{},
|
|
&model.PackageDigest{},
|
|
); err != nil {
|
|
logrus.
|
|
WithField("path", "NewManifestDBHandler").
|
|
WithField("method", "AutoMigrate").
|
|
Panic(err)
|
|
}
|
|
|
|
return &dbManifests{db: tx}
|
|
}
|