Files
silo-server/internal/adminjob/library_delete.go
T

116 lines
3.4 KiB
Go

package adminjob
import (
"context"
"encoding/json"
"fmt"
"github.com/Silo-Server/silo-server/internal/catalog"
"github.com/Silo-Server/silo-server/internal/sections"
)
const JobTypeDeleteLibrary = "delete_library"
type DeleteLibraryRequest struct {
LibraryID int `json:"library_id"`
LibraryName string `json:"library_name"`
}
type DeleteLibraryResult struct {
LibraryID int `json:"library_id"`
LibraryName string `json:"library_name"`
DeletedMediaFiles int `json:"deleted_media_files"`
DeletedItemLinks int `json:"deleted_item_links"`
DeletedOrphanedItems int `json:"deleted_orphaned_items"`
DeletedS3Objects int `json:"deleted_s3_objects"`
ImageCleanupQueued bool `json:"image_cleanup_queued"`
ImageCleanupJobID string `json:"image_cleanup_job_id,omitempty"`
ImageCleanupDirs int `json:"image_cleanup_dirs"`
orphanedImageDirs []string
}
// S3PrefixDeleter can delete all objects under a prefix.
type S3PrefixDeleter interface {
DeletePrefix(ctx context.Context, bucket, prefix string) (int, error)
Bucket() string
}
type deleteLibraryExecutor interface {
Execute(ctx context.Context, req DeleteLibraryRequest, progress func(current, total int, message string)) (*DeleteLibraryResult, error)
}
type LibraryDeleteExecutor struct {
folderRepo *catalog.FolderRepository
sectionRepo *sections.Repository
}
func NewLibraryDeleteExecutor(folderRepo *catalog.FolderRepository, sectionRepo *sections.Repository) *LibraryDeleteExecutor {
return &LibraryDeleteExecutor{folderRepo: folderRepo, sectionRepo: sectionRepo}
}
func (e *LibraryDeleteExecutor) Execute(
ctx context.Context,
req DeleteLibraryRequest,
progress func(current, total int, message string),
) (*DeleteLibraryResult, error) {
if e == nil || e.folderRepo == nil {
return nil, fmt.Errorf("library delete executor is not configured")
}
if req.LibraryID <= 0 {
return nil, fmt.Errorf("library_id is required")
}
if progress != nil {
progress(0, 5, "Counting library contents")
}
stats, err := e.folderRepo.DeleteWithStats(ctx, req.LibraryID, func(current, total int, message string) {
if progress != nil {
progress(current, total+2, message)
}
})
if err != nil {
return nil, err
}
if e.sectionRepo != nil {
if progress != nil {
progress(4, 5, "Cleaning up generated home sections")
}
if err := e.sectionRepo.DeleteGeneratedHomeLibraryRecentSections(ctx, req.LibraryID); err != nil {
return nil, fmt.Errorf("deleting generated home sections: %w", err)
}
}
if progress != nil {
progress(5, 5, "Library deletion completed")
}
name := req.LibraryName
if name == "" {
name = stats.LibraryName
}
return &DeleteLibraryResult{
LibraryID: req.LibraryID,
LibraryName: name,
DeletedMediaFiles: stats.MediaFiles,
DeletedItemLinks: stats.MediaItemLinks,
DeletedOrphanedItems: stats.OrphanedItems,
DeletedS3Objects: 0,
ImageCleanupDirs: len(stats.OrphanedImageDirs),
orphanedImageDirs: append([]string(nil), stats.OrphanedImageDirs...),
}, nil
}
func decodeDeleteLibraryRequest(data json.RawMessage) (DeleteLibraryRequest, error) {
var req DeleteLibraryRequest
if len(data) == 0 {
return req, fmt.Errorf("missing delete library payload")
}
if err := json.Unmarshal(data, &req); err != nil {
return req, fmt.Errorf("invalid delete library request payload: %w", err)
}
return req, nil
}