116 lines
3.4 KiB
Go
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
|
|
}
|