feat(Lactose): support restoring soft-deleted assets via update endpoints
This commit is contained in:
@@ -239,8 +239,18 @@ public class AssetController(
|
|||||||
asset.Visibility = dto.Visibility ?? asset.Visibility;
|
asset.Visibility = dto.Visibility ?? asset.Visibility;
|
||||||
log += $"Visibility: {asset.Visibility} -> {dto.Visibility ?? asset.Visibility}\n";
|
log += $"Visibility: {asset.Visibility} -> {dto.Visibility ?? asset.Visibility}\n";
|
||||||
|
|
||||||
asset.DeletedAt = dto.DeletedAt ?? asset.DeletedAt;
|
if (dto.IsDeleted.HasValue) {
|
||||||
log += $"DeletedAt: {asset.DeletedAt} -> {dto.DeletedAt ?? asset.DeletedAt}\n";
|
// Restoring a soft-deleted asset requires its source file to still exist on disk;
|
||||||
|
// otherwise the crawl/integrity jobs would silently re-delete it on their next run.
|
||||||
|
if (!dto.IsDeleted.Value && asset.DeletedAt != null && !System.IO.File.Exists(asset.OriginalPath)) {
|
||||||
|
logger.LogWarning($"Cannot restore asset {id}: source file '{asset.OriginalPath}' does not exist on disk!");
|
||||||
|
return Conflict();
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? newDeletedAt = dto.IsDeleted.Value ? DateTime.UtcNow : null;
|
||||||
|
log += $"DeletedAt: {asset.DeletedAt} -> {newDeletedAt}\n";
|
||||||
|
asset.DeletedAt = newDeletedAt;
|
||||||
|
}
|
||||||
|
|
||||||
if (accesslevel == EAccessLevel.Admin) {
|
if (accesslevel == EAccessLevel.Admin) {
|
||||||
asset.UploadedBy = dto.UploadedBy ?? asset.UploadedBy;
|
asset.UploadedBy = dto.UploadedBy ?? asset.UploadedBy;
|
||||||
@@ -286,10 +296,17 @@ public class AssetController(
|
|||||||
? await FilterForMaintainerAsync(enumerable, uid.Value, cancellationToken)
|
? await FilterForMaintainerAsync(enumerable, uid.Value, cancellationToken)
|
||||||
: enumerable.Where(x => x.UploadedBy == uid).ToList();
|
: enumerable.Where(x => x.UploadedBy == uid).ToList();
|
||||||
|
|
||||||
|
if (bulkDto.Data.IsDeleted == false) {
|
||||||
|
var result = ApplyBulkRestore(enumerable, bulkDto.Data);
|
||||||
|
assetRepository.UpdateBulk(enumerable);
|
||||||
|
await assetRepository.SaveAsync(cancellationToken);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
enumerable.ForEach(
|
enumerable.ForEach(
|
||||||
x => {
|
x => {
|
||||||
x.Visibility = bulkDto.Data.Visibility ?? x.Visibility;
|
x.Visibility = bulkDto.Data.Visibility ?? x.Visibility;
|
||||||
x.DeletedAt = bulkDto.Data.DeletedAt ?? x.DeletedAt;
|
x.DeletedAt = bulkDto.Data.IsDeleted == true ? DateTime.UtcNow : x.DeletedAt;
|
||||||
|
|
||||||
if (accesslevel == EAccessLevel.Admin) {
|
if (accesslevel == EAccessLevel.Admin) {
|
||||||
x.UploadedBy = bulkDto.Data.UploadedBy ?? x.UploadedBy;
|
x.UploadedBy = bulkDto.Data.UploadedBy ?? x.UploadedBy;
|
||||||
@@ -302,6 +319,38 @@ public class AssetController(
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a restore request (<c>IsDeleted</c> = <see langword="false"/>) to the given assets.
|
||||||
|
/// Assets whose source file no longer exists on disk are skipped and reported instead of restored,
|
||||||
|
/// since the crawl/integrity jobs would re-delete them otherwise. All other update fields are still
|
||||||
|
/// applied to the assets that are not skipped.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assets">The assets selected for the bulk update.</param>
|
||||||
|
/// <param name="data">The shared update values.</param>
|
||||||
|
/// <returns>The outcome listing restored IDs and IDs skipped due to missing files.</returns>
|
||||||
|
private BulkRestoreResultDto ApplyBulkRestore(List<Asset> assets, AssetUpdateDto data) {
|
||||||
|
BulkRestoreResultDto result = new();
|
||||||
|
|
||||||
|
foreach (var x in assets) {
|
||||||
|
if (x.DeletedAt != null && !System.IO.File.Exists(x.OriginalPath)) {
|
||||||
|
result.SkippedMissingFile.Add(x.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasRestored = x.DeletedAt != null;
|
||||||
|
|
||||||
|
x.Visibility = data.Visibility ?? x.Visibility;
|
||||||
|
x.DeletedAt = null;
|
||||||
|
|
||||||
|
if (wasRestored) result.Restored.Add(x.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.SkippedMissingFile.Count > 0)
|
||||||
|
logger.LogWarning($"Bulk restore: {result.SkippedMissingFile.Count} asset(s) skipped because their source files are missing on disk");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Soft-deletes an asset by its ID.
|
/// Soft-deletes an asset by its ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user