mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-02 20:26:36 +08:00
split media recycle service
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
"github.com/ShukeBta/MediaStationGo/internal/config"
|
"github.com/ShukeBta/MediaStationGo/internal/config"
|
||||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||||
@@ -268,100 +267,3 @@ func (s *MediaService) GetMedia(ctx context.Context, id string) (*model.Media, e
|
|||||||
*media = items[0]
|
*media = items[0]
|
||||||
return media, nil
|
return media, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxRecycleBinRecords = 200
|
|
||||||
|
|
||||||
// SoftDelete moves a media row to the recycle bin (gorm soft delete).
|
|
||||||
// The on-disk file is kept; admins can purge it later.
|
|
||||||
func (s *MediaService) SoftDelete(ctx context.Context, id string) error {
|
|
||||||
media, err := s.repo.Media.FindByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if media != nil && isCloudMediaPath(media.Path) {
|
|
||||||
err := s.repo.DB.WithContext(ctx).Unscoped().Where("id = ?", id).Delete(&model.Media{}).Error
|
|
||||||
if err == nil {
|
|
||||||
s.invalidateMediaCache(ctx)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = s.repo.DB.WithContext(ctx).Where("id = ?", id).Delete(&model.Media{}).Error
|
|
||||||
if err == nil {
|
|
||||||
if pruneErr := pruneRecycleBinRows(ctx, s.repo.DB, maxRecycleBinRecords); pruneErr != nil {
|
|
||||||
return pruneErr
|
|
||||||
}
|
|
||||||
s.invalidateMediaCache(ctx)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreDeleted unsets DeletedAt for a single media row.
|
|
||||||
func (s *MediaService) RestoreDeleted(ctx context.Context, id string) error {
|
|
||||||
err := s.repo.DB.WithContext(ctx).Unscoped().Model(&model.Media{}).
|
|
||||||
Where("id = ?", id).Update("deleted_at", nil).Error
|
|
||||||
if err == nil {
|
|
||||||
s.invalidateMediaCache(ctx)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRecycleBin returns every soft-deleted row, newest first.
|
|
||||||
func (s *MediaService) ListRecycleBin(ctx context.Context, limit int) ([]model.Media, error) {
|
|
||||||
if err := pruneRecycleBinRows(ctx, s.repo.DB, maxRecycleBinRecords); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if limit <= 0 || limit > maxRecycleBinRecords {
|
|
||||||
limit = maxRecycleBinRecords
|
|
||||||
}
|
|
||||||
var rows []model.Media
|
|
||||||
err := s.repo.DB.Unscoped().
|
|
||||||
Where("deleted_at IS NOT NULL").
|
|
||||||
Order("deleted_at desc").
|
|
||||||
Limit(limit).
|
|
||||||
Find(&rows).Error
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func pruneRecycleBinRows(ctx context.Context, db *gorm.DB, keep int) error {
|
|
||||||
if db == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if keep <= 0 {
|
|
||||||
keep = maxRecycleBinRecords
|
|
||||||
}
|
|
||||||
var rows []struct {
|
|
||||||
ID string
|
|
||||||
}
|
|
||||||
if err := db.WithContext(ctx).Unscoped().
|
|
||||||
Model(&model.Media{}).
|
|
||||||
Select("id").
|
|
||||||
Where("deleted_at IS NOT NULL").
|
|
||||||
Order("deleted_at desc").
|
|
||||||
Limit(100000).
|
|
||||||
Offset(keep).
|
|
||||||
Find(&rows).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(rows) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ids := make([]string, 0, len(rows))
|
|
||||||
for _, row := range rows {
|
|
||||||
if row.ID != "" {
|
|
||||||
ids = append(ids, row.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(ids) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return db.WithContext(ctx).Unscoped().Where("id IN ?", ids).Delete(&model.Media{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// PurgeDeleted permanently removes a soft-deleted row from the database.
|
|
||||||
func (s *MediaService) PurgeDeleted(ctx context.Context, id string) error {
|
|
||||||
err := s.repo.DB.WithContext(ctx).Unscoped().Where("id = ?", id).Delete(&model.Media{}).Error
|
|
||||||
if err == nil {
|
|
||||||
s.invalidateMediaCache(ctx)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxRecycleBinRecords = 200
|
||||||
|
|
||||||
|
// SoftDelete moves a media row to the recycle bin (gorm soft delete).
|
||||||
|
// The on-disk file is kept; admins can purge it later.
|
||||||
|
func (s *MediaService) SoftDelete(ctx context.Context, id string) error {
|
||||||
|
media, err := s.repo.Media.FindByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if media != nil && isCloudMediaPath(media.Path) {
|
||||||
|
err := s.repo.DB.WithContext(ctx).Unscoped().Where("id = ?", id).Delete(&model.Media{}).Error
|
||||||
|
if err == nil {
|
||||||
|
s.invalidateMediaCache(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.repo.DB.WithContext(ctx).Where("id = ?", id).Delete(&model.Media{}).Error
|
||||||
|
if err == nil {
|
||||||
|
if pruneErr := pruneRecycleBinRows(ctx, s.repo.DB, maxRecycleBinRecords); pruneErr != nil {
|
||||||
|
return pruneErr
|
||||||
|
}
|
||||||
|
s.invalidateMediaCache(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreDeleted unsets DeletedAt for a single media row.
|
||||||
|
func (s *MediaService) RestoreDeleted(ctx context.Context, id string) error {
|
||||||
|
err := s.repo.DB.WithContext(ctx).Unscoped().Model(&model.Media{}).
|
||||||
|
Where("id = ?", id).Update("deleted_at", nil).Error
|
||||||
|
if err == nil {
|
||||||
|
s.invalidateMediaCache(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRecycleBin returns every soft-deleted row, newest first.
|
||||||
|
func (s *MediaService) ListRecycleBin(ctx context.Context, limit int) ([]model.Media, error) {
|
||||||
|
if err := pruneRecycleBinRows(ctx, s.repo.DB, maxRecycleBinRecords); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if limit <= 0 || limit > maxRecycleBinRecords {
|
||||||
|
limit = maxRecycleBinRecords
|
||||||
|
}
|
||||||
|
var rows []model.Media
|
||||||
|
err := s.repo.DB.Unscoped().
|
||||||
|
Where("deleted_at IS NOT NULL").
|
||||||
|
Order("deleted_at desc").
|
||||||
|
Limit(limit).
|
||||||
|
Find(&rows).Error
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func pruneRecycleBinRows(ctx context.Context, db *gorm.DB, keep int) error {
|
||||||
|
if db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if keep <= 0 {
|
||||||
|
keep = maxRecycleBinRecords
|
||||||
|
}
|
||||||
|
var rows []struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
if err := db.WithContext(ctx).Unscoped().
|
||||||
|
Model(&model.Media{}).
|
||||||
|
Select("id").
|
||||||
|
Where("deleted_at IS NOT NULL").
|
||||||
|
Order("deleted_at desc").
|
||||||
|
Limit(100000).
|
||||||
|
Offset(keep).
|
||||||
|
Find(&rows).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(rows) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ids := make([]string, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.ID != "" {
|
||||||
|
ids = append(ids, row.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return db.WithContext(ctx).Unscoped().Where("id IN ?", ids).Delete(&model.Media{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// PurgeDeleted permanently removes a soft-deleted row from the database.
|
||||||
|
func (s *MediaService) PurgeDeleted(ctx context.Context, id string) error {
|
||||||
|
err := s.repo.DB.WithContext(ctx).Unscoped().Where("id = ?", id).Delete(&model.Media{}).Error
|
||||||
|
if err == nil {
|
||||||
|
s.invalidateMediaCache(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user