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,209 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/tenant/repository/generated"
|
||||
)
|
||||
|
||||
type ImageAsset struct {
|
||||
ID int64
|
||||
TenantID int64
|
||||
FolderID *int64
|
||||
ObjectKey string
|
||||
Name string
|
||||
SizeBytes int64
|
||||
Status string
|
||||
CreatedBy int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ImageAssetListParams struct {
|
||||
TenantID int64
|
||||
FolderID *int64
|
||||
SearchQuery *string
|
||||
PageSize int
|
||||
PageOffset int
|
||||
}
|
||||
|
||||
type InsertImageAssetInput struct {
|
||||
TenantID int64
|
||||
FolderID *int64
|
||||
ObjectKey string
|
||||
Name string
|
||||
SizeBytes int64
|
||||
Status string
|
||||
CreatedBy int64
|
||||
}
|
||||
|
||||
type FolderImageSummary struct {
|
||||
ID int64
|
||||
ObjectKey string
|
||||
SizeBytes int64
|
||||
}
|
||||
|
||||
type ImageRepository interface {
|
||||
List(ctx context.Context, params ImageAssetListParams) ([]ImageAsset, int, error)
|
||||
Get(ctx context.Context, tenantID, id int64) (*ImageAsset, error)
|
||||
Insert(ctx context.Context, input InsertImageAssetInput) (int64, error)
|
||||
UpdateStatus(ctx context.Context, tenantID, id int64, status string) error
|
||||
MarkDeleted(ctx context.Context, tenantID, id int64) error
|
||||
UpdateName(ctx context.Context, tenantID, id int64, name string) error
|
||||
UpdateFolder(ctx context.Context, tenantID, id int64, folderID *int64) error
|
||||
ListActiveByFolder(ctx context.Context, tenantID, folderID int64) ([]FolderImageSummary, error)
|
||||
MarkImagesInFolderPendingDelete(ctx context.Context, tenantID, folderID int64) error
|
||||
CountUncategorized(ctx context.Context, tenantID int64) (int, error)
|
||||
}
|
||||
|
||||
type imageRepository struct {
|
||||
q generated.Querier
|
||||
}
|
||||
|
||||
func NewImageRepository(db generated.DBTX) ImageRepository {
|
||||
return &imageRepository{q: newQuerier(db)}
|
||||
}
|
||||
|
||||
func (r *imageRepository) List(ctx context.Context, params ImageAssetListParams) ([]ImageAsset, int, error) {
|
||||
listParams := generated.ListImageAssetsParams{
|
||||
TenantID: params.TenantID,
|
||||
FolderID: pgInt8(params.FolderID),
|
||||
PageOffset: int32(params.PageOffset),
|
||||
PageSize: int32(params.PageSize),
|
||||
SearchQuery: pgText(params.SearchQuery),
|
||||
}
|
||||
|
||||
rows, err := r.q.ListImageAssets(ctx, listParams)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
total, err := r.q.CountImageAssets(ctx, generated.CountImageAssetsParams{
|
||||
TenantID: params.TenantID,
|
||||
FolderID: listParams.FolderID,
|
||||
SearchQuery: listParams.SearchQuery,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
assets := make([]ImageAsset, len(rows))
|
||||
for i, row := range rows {
|
||||
assets[i] = ImageAsset{
|
||||
ID: row.ID,
|
||||
TenantID: row.TenantID,
|
||||
FolderID: nullableInt64(row.FolderID),
|
||||
ObjectKey: row.ObjectKey,
|
||||
Name: row.Name,
|
||||
SizeBytes: row.SizeBytes,
|
||||
Status: row.Status,
|
||||
CreatedBy: row.CreatedBy,
|
||||
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
return assets, int(total), nil
|
||||
}
|
||||
|
||||
func (r *imageRepository) Get(ctx context.Context, tenantID, id int64) (*ImageAsset, error) {
|
||||
row, err := r.q.GetImageAsset(ctx, generated.GetImageAssetParams{
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageAsset{
|
||||
ID: row.ID,
|
||||
TenantID: row.TenantID,
|
||||
FolderID: nullableInt64(row.FolderID),
|
||||
ObjectKey: row.ObjectKey,
|
||||
Name: row.Name,
|
||||
SizeBytes: row.SizeBytes,
|
||||
Status: row.Status,
|
||||
CreatedBy: row.CreatedBy,
|
||||
CreatedAt: timeFromTimestamp(row.CreatedAt),
|
||||
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *imageRepository) Insert(ctx context.Context, input InsertImageAssetInput) (int64, error) {
|
||||
return r.q.InsertImageAsset(ctx, generated.InsertImageAssetParams{
|
||||
TenantID: input.TenantID,
|
||||
FolderID: pgInt8(input.FolderID),
|
||||
ObjectKey: input.ObjectKey,
|
||||
Name: input.Name,
|
||||
SizeBytes: input.SizeBytes,
|
||||
Status: input.Status,
|
||||
CreatedBy: input.CreatedBy,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) UpdateStatus(ctx context.Context, tenantID, id int64, status string) error {
|
||||
return r.q.UpdateImageAssetStatus(ctx, generated.UpdateImageAssetStatusParams{
|
||||
Status: status,
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) MarkDeleted(ctx context.Context, tenantID, id int64) error {
|
||||
return r.q.UpdateImageAssetDeleted(ctx, generated.UpdateImageAssetDeletedParams{
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) UpdateName(ctx context.Context, tenantID, id int64, name string) error {
|
||||
return r.q.UpdateImageAssetName(ctx, generated.UpdateImageAssetNameParams{
|
||||
Name: name,
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) UpdateFolder(ctx context.Context, tenantID, id int64, folderID *int64) error {
|
||||
return r.q.UpdateImageAssetFolder(ctx, generated.UpdateImageAssetFolderParams{
|
||||
FolderID: pgInt8(folderID),
|
||||
ID: id,
|
||||
TenantID: tenantID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) ListActiveByFolder(ctx context.Context, tenantID, folderID int64) ([]FolderImageSummary, error) {
|
||||
rows, err := r.q.ListActiveImagesByFolder(ctx, generated.ListActiveImagesByFolderParams{
|
||||
TenantID: tenantID,
|
||||
FolderID: pgInt8(&folderID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]FolderImageSummary, len(rows))
|
||||
for i, row := range rows {
|
||||
result[i] = FolderImageSummary{
|
||||
ID: row.ID,
|
||||
ObjectKey: row.ObjectKey,
|
||||
SizeBytes: row.SizeBytes,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *imageRepository) MarkImagesInFolderPendingDelete(ctx context.Context, tenantID, folderID int64) error {
|
||||
return r.q.MarkImagesInFolderPendingDelete(ctx, generated.MarkImagesInFolderPendingDeleteParams{
|
||||
TenantID: tenantID,
|
||||
FolderID: pgInt8(&folderID),
|
||||
})
|
||||
}
|
||||
|
||||
func (r *imageRepository) CountUncategorized(ctx context.Context, tenantID int64) (int, error) {
|
||||
count, err := r.q.CountUncategorizedImages(ctx, tenantID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
Reference in New Issue
Block a user