feat: add image management functionality
- Implemented image folder repository with CRUD operations. - Added image reference repository for managing image references to articles. - Created image repository for handling image assets, including listing, inserting, updating, and deleting images. - Introduced image usage repository to track storage usage and quotas for tenants. - Added SQL queries for image assets, folders, references, and usage. - Developed image handler for HTTP endpoints to manage images and folders. - Created database migration scripts for image-related tables and structures.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||
)
|
||||
|
||||
type ImageStorageUsage struct {
|
||||
UsedBytes int64
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type QuotaReserveResult struct {
|
||||
UsedBytes int64
|
||||
QuotaBytes int64
|
||||
}
|
||||
|
||||
type ImageUsageRepository interface {
|
||||
EnsureRow(ctx context.Context, tenantID int64) error
|
||||
Get(ctx context.Context, tenantID int64) (*ImageStorageUsage, error)
|
||||
Release(ctx context.Context, tenantID, releaseBytes int64) error
|
||||
AtomicReserve(ctx context.Context, tenantID, sizeBytes int64) (*QuotaReserveResult, error)
|
||||
}
|
||||
|
||||
type imageUsageRepository struct {
|
||||
q generated.Querier
|
||||
db generated.DBTX
|
||||
}
|
||||
|
||||
func NewImageUsageRepository(db generated.DBTX) ImageUsageRepository {
|
||||
return &imageUsageRepository{q: newQuerier(db), db: db}
|
||||
}
|
||||
|
||||
func (r *imageUsageRepository) EnsureRow(ctx context.Context, tenantID int64) error {
|
||||
return r.q.EnsureImageStorageUsageRow(ctx, tenantID)
|
||||
}
|
||||
|
||||
func (r *imageUsageRepository) Get(ctx context.Context, tenantID int64) (*ImageStorageUsage, error) {
|
||||
row, err := r.q.GetImageStorageUsage(ctx, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ImageStorageUsage{
|
||||
UsedBytes: row.UsedBytes,
|
||||
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *imageUsageRepository) Release(ctx context.Context, tenantID, releaseBytes int64) error {
|
||||
return r.q.ReleaseImageStorageQuota(ctx, generated.ReleaseImageStorageQuotaParams{
|
||||
ReleaseBytes: releaseBytes,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageUsageRepository) AtomicReserve(ctx context.Context, tenantID, sizeBytes int64) (*QuotaReserveResult, error) {
|
||||
row := r.db.QueryRow(ctx, `
|
||||
WITH plan AS (
|
||||
SELECT COALESCE((p.quota_policy_json ->> 'image_storage_bytes')::BIGINT, 104857600) AS quota_bytes
|
||||
FROM tenant_plan_subscriptions s
|
||||
JOIN plans p ON p.id = s.plan_id
|
||||
WHERE s.tenant_id = $1
|
||||
AND s.status = 'active'
|
||||
AND s.deleted_at IS NULL
|
||||
LIMIT 1
|
||||
)
|
||||
UPDATE tenant_image_storage_usage u
|
||||
SET used_bytes = u.used_bytes + $2,
|
||||
updated_at = NOW()
|
||||
FROM plan
|
||||
WHERE u.tenant_id = $1
|
||||
AND u.used_bytes + $2 <= plan.quota_bytes
|
||||
RETURNING u.used_bytes, plan.quota_bytes
|
||||
`, tenantID, sizeBytes)
|
||||
|
||||
var result QuotaReserveResult
|
||||
if err := row.Scan(&result.UsedBytes, &result.QuotaBytes); err != nil {
|
||||
return nil, fmt.Errorf("reserve image storage quota: %w", err)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user