feat(Lactose): support restoring soft-deleted assets via update endpoints

This commit is contained in:
2026-08-22 00:43:31 +02:00
parent 594fe7a977
commit a8122ebc39
+52 -3
View File
@@ -239,8 +239,18 @@ public class AssetController(
asset.Visibility = dto.Visibility ?? asset.Visibility;
log += $"Visibility: {asset.Visibility} -> {dto.Visibility ?? asset.Visibility}\n";
asset.DeletedAt = dto.DeletedAt ?? asset.DeletedAt;
log += $"DeletedAt: {asset.DeletedAt} -> {dto.DeletedAt ?? asset.DeletedAt}\n";
if (dto.IsDeleted.HasValue) {
// 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) {
asset.UploadedBy = dto.UploadedBy ?? asset.UploadedBy;
@@ -286,10 +296,17 @@ public class AssetController(
? await FilterForMaintainerAsync(enumerable, uid.Value, cancellationToken)
: 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(
x => {
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) {
x.UploadedBy = bulkDto.Data.UploadedBy ?? x.UploadedBy;
@@ -302,6 +319,38 @@ public class AssetController(
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>
/// Soft-deletes an asset by its ID.
/// </summary>