185 lines
5.9 KiB
Go
185 lines
5.9 KiB
Go
package persistence
|
|
|
|
import (
|
|
domain "RenewCMS/internal/domain/image"
|
|
"RenewCMS/internal/infrastructure/persistence/mappers"
|
|
entity "RenewCMS/internal/infrastructure/persistence/models"
|
|
"errors"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
type VariantFileRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
var _ domain.VariantFileRepository = &VariantFileRepository{}
|
|
var _ domain.VariantCacheRepository = &VariantFileRepository{}
|
|
|
|
func NewVariantFileRepository(db *gorm.DB) *VariantFileRepository {
|
|
return &VariantFileRepository{db}
|
|
}
|
|
|
|
func (v *VariantFileRepository) Find(variantName string, sourceName string) (domain.VariantFile, bool, error) {
|
|
var model entity.ImageVariantFile
|
|
err := v.db.Where("variant_name = ? AND source_name = ?", variantName, sourceName).First(&model).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return domain.VariantFile{}, false, nil
|
|
}
|
|
if err != nil {
|
|
return domain.VariantFile{}, false, err
|
|
}
|
|
return mappers.VariantFileToDomain(model), true, nil
|
|
}
|
|
|
|
func (v *VariantFileRepository) Save(file domain.VariantFile) (domain.VariantFile, error) {
|
|
model := mappers.VariantFileToModel(file)
|
|
model.ID = 0
|
|
upsert := clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "variant_name"}, {Name: "source_name"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{"cached_name", "content_type", "size", "fingerprint"}),
|
|
}
|
|
query := v.db.Clauses(upsert)
|
|
err := query.Create(&model).Error
|
|
if err != nil {
|
|
return domain.VariantFile{}, err
|
|
}
|
|
saved, _, err := v.Find(file.VariantName, file.SourceName)
|
|
return saved, err
|
|
}
|
|
|
|
func (v *VariantFileRepository) ListByVariant(variantName string) ([]domain.VariantFile, error) {
|
|
return v.list("variant_name = ?", variantName)
|
|
}
|
|
|
|
func (v *VariantFileRepository) ListBySource(sourceName string) ([]domain.VariantFile, error) {
|
|
return v.list("source_name = ?", sourceName)
|
|
}
|
|
|
|
func (v *VariantFileRepository) list(condition string, value string) ([]domain.VariantFile, error) {
|
|
var found []entity.ImageVariantFile
|
|
if err := v.db.Where(condition, value).Order("id asc").Find(&found).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
files := make([]domain.VariantFile, 0, len(found))
|
|
for _, model := range found {
|
|
files = append(files, mappers.VariantFileToDomain(model))
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
func (v *VariantFileRepository) Delete(id uint32) error {
|
|
return v.db.Delete(&entity.ImageVariantFile{}, id).Error
|
|
}
|
|
|
|
func (v *VariantFileRepository) TotalsBySource() (map[string]domain.VariantFileTotals, error) {
|
|
var rows []struct {
|
|
SourceName string
|
|
Files int
|
|
Bytes int64
|
|
}
|
|
query := v.db.Model(&entity.ImageVariantFile{})
|
|
query = query.Select("source_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
|
|
query = query.Group("source_name")
|
|
err := query.Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
totals := make(map[string]domain.VariantFileTotals, len(rows))
|
|
for _, row := range rows {
|
|
totals[row.SourceName] = domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}
|
|
}
|
|
return totals, nil
|
|
}
|
|
|
|
const (
|
|
unusedCondition = "variant_name IN ? AND COALESCE(last_requested_at, created_at) < ?"
|
|
topSourcesOrder = "bytes DESC, source_name ASC"
|
|
)
|
|
|
|
func (v *VariantFileRepository) UnusedTotals(filter domain.UnusedFilter) (domain.VariantFileTotals, error) {
|
|
if len(filter.VariantNames) == 0 {
|
|
return domain.VariantFileTotals{}, nil
|
|
}
|
|
var row struct {
|
|
Files int
|
|
Bytes int64
|
|
}
|
|
query := v.db.Model(&entity.ImageVariantFile{})
|
|
query = query.Select("COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
|
|
query = query.Where(unusedCondition, filter.VariantNames, filter.Cutoff.UTC())
|
|
err := query.Scan(&row).Error
|
|
if err != nil {
|
|
return domain.VariantFileTotals{}, err
|
|
}
|
|
return domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}, nil
|
|
}
|
|
|
|
func (v *VariantFileRepository) TopSources(limit int) ([]domain.SourceTotals, error) {
|
|
var rows []struct {
|
|
SourceName string
|
|
Files int
|
|
Bytes int64
|
|
}
|
|
query := v.db.Model(&entity.ImageVariantFile{})
|
|
query = query.Select("source_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
|
|
query = query.Group("source_name")
|
|
query = query.Order(topSourcesOrder)
|
|
query = query.Limit(limit)
|
|
err := query.Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sources := make([]domain.SourceTotals, 0, len(rows))
|
|
for _, row := range rows {
|
|
sources = append(sources, domain.SourceTotals{SourceName: row.SourceName, Files: row.Files, Bytes: row.Bytes})
|
|
}
|
|
return sources, nil
|
|
}
|
|
|
|
func (v *VariantFileRepository) ListPage(filter domain.VariantFileFilter, afterID uint32, limit int) ([]domain.VariantFile, error) {
|
|
statement := v.db.Where("id > ?", afterID)
|
|
if filter.VariantName != "" {
|
|
statement = statement.Where("variant_name = ?", filter.VariantName)
|
|
}
|
|
if filter.SourceName != "" {
|
|
statement = statement.Where("source_name = ?", filter.SourceName)
|
|
}
|
|
if filter.Unused != nil {
|
|
if len(filter.Unused.VariantNames) == 0 {
|
|
return []domain.VariantFile{}, nil
|
|
}
|
|
statement = statement.Where(unusedCondition, filter.Unused.VariantNames, filter.Unused.Cutoff.UTC())
|
|
}
|
|
var found []entity.ImageVariantFile
|
|
if err := statement.Order("id asc").Limit(limit).Find(&found).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
files := make([]domain.VariantFile, 0, len(found))
|
|
for _, model := range found {
|
|
files = append(files, mappers.VariantFileToDomain(model))
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
func (v *VariantFileRepository) Totals() (map[string]domain.VariantFileTotals, error) {
|
|
var rows []struct {
|
|
VariantName string
|
|
Files int
|
|
Bytes int64
|
|
}
|
|
query := v.db.Model(&entity.ImageVariantFile{})
|
|
query = query.Select("variant_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
|
|
query = query.Group("variant_name")
|
|
err := query.Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
totals := make(map[string]domain.VariantFileTotals, len(rows))
|
|
for _, row := range rows {
|
|
totals[row.VariantName] = domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}
|
|
}
|
|
return totals, nil
|
|
}
|