27389164b0
- 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.
85 lines
2.3 KiB
Go
85 lines
2.3 KiB
Go
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
|
|
}
|