✨ Recycled files action
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -136,6 +137,7 @@ public class FileController(
|
||||
[HttpGet("me")]
|
||||
public async Task<ActionResult<List<CloudFile>>> GetMyFiles(
|
||||
[FromQuery] Guid? pool,
|
||||
[FromQuery] bool recycled = false,
|
||||
[FromQuery] int offset = 0,
|
||||
[FromQuery] int take = 20
|
||||
)
|
||||
@@ -144,6 +146,7 @@ public class FileController(
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var query = db.Files
|
||||
.Where(e => e.IsMarkedRecycle == recycled)
|
||||
.Where(e => e.AccountId == accountId)
|
||||
.Include(e => e.Pool)
|
||||
.OrderByDescending(e => e.CreatedAt)
|
||||
@@ -182,4 +185,24 @@ public class FileController(
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpDelete("me/recycle")]
|
||||
public async Task<ActionResult> DeleteMyRecycledFiles()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var count = await fs.DeleteAccountRecycledFilesAsync(accountId);
|
||||
return Ok(new { Count = count });
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpDelete("recycle")]
|
||||
[RequiredPermission("maintenance", "files.delete.recycle")]
|
||||
public async Task<ActionResult> DeleteAllRecycledFiles()
|
||||
{
|
||||
var count = await fs.DeleteAllRecycledFilesAsync();
|
||||
return Ok(new { Count = count });
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ namespace DysonNetwork.Drive.Storage;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/pools")]
|
||||
public class FilePoolController(AppDatabase db) : ControllerBase
|
||||
public class FilePoolController(AppDatabase db, FileService fs) : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
@@ -28,4 +28,19 @@ public class FilePoolController(AppDatabase db) : ControllerBase
|
||||
|
||||
return Ok(pools);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpDelete("{id:guid}/recycle")]
|
||||
public async Task<ActionResult> DeleteFilePoolRecycledFiles(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
|
||||
var pool = await fs.GetPoolAsync(id);
|
||||
if (pool is null) return NotFound();
|
||||
if (!currentUser.IsSuperuser && pool.AccountId != accountId) return Unauthorized();
|
||||
|
||||
var count = await fs.DeletePoolRecycledFilesAsync(id);
|
||||
return Ok(new { Count = count });
|
||||
}
|
||||
}
|
@@ -370,7 +370,7 @@ public class FileService(
|
||||
|
||||
if (File.Exists(thumbnailPath))
|
||||
{
|
||||
uploads.Add((thumbnailPath, ".thumbnail.webp", "image/webp", true));
|
||||
uploads.Add((thumbnailPath, ".thumbnail", "image/webp", true));
|
||||
hasThumbnail = true;
|
||||
}
|
||||
else
|
||||
@@ -544,11 +544,11 @@ public class FileService(
|
||||
db.Remove(file);
|
||||
await db.SaveChangesAsync();
|
||||
await _PurgeCacheAsync(file.Id);
|
||||
|
||||
|
||||
await DeleteFileDataAsync(file);
|
||||
}
|
||||
|
||||
public async Task DeleteFileDataAsync(CloudFile file)
|
||||
private async Task DeleteFileDataAsync(CloudFile file)
|
||||
{
|
||||
if (file.StorageId is null) return;
|
||||
if (!file.PoolId.HasValue) return;
|
||||
@@ -581,7 +581,6 @@ public class FileService(
|
||||
|
||||
if (file.HasCompression)
|
||||
{
|
||||
// Also remove the compressed version if it exists
|
||||
try
|
||||
{
|
||||
await client.RemoveObjectAsync(
|
||||
@@ -594,6 +593,20 @@ public class FileService(
|
||||
logger.LogWarning("Failed to delete compressed version of file {fileId}", file.Id);
|
||||
}
|
||||
}
|
||||
if (file.HasThumbnail)
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.RemoveObjectAsync(
|
||||
new RemoveObjectArgs().WithBucket(bucket).WithObject(objectId + ".thumbnail")
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors when deleting thumbnail
|
||||
logger.LogWarning("Failed to delete thumbnail of file {fileId}", file.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<FilePool?> GetPoolAsync(Guid destination)
|
||||
@@ -735,6 +748,51 @@ public class FileService(
|
||||
if (fieldName.EndsWith("-data")) return true;
|
||||
return gpsFields.Any(gpsField => fieldName.StartsWith(gpsField, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAccountRecycledFilesAsync(Guid accountId)
|
||||
{
|
||||
var files = await db.Files
|
||||
.Where(f => f.AccountId == accountId && f.IsMarkedRecycle)
|
||||
.ToListAsync();
|
||||
var count = files.Count;
|
||||
var tasks = files.Select(DeleteFileDataAsync);
|
||||
await Task.WhenAll(tasks);
|
||||
var fileIds = files.Select(f => f.Id).ToList();
|
||||
await _PurgeCacheRangeAsync(fileIds);
|
||||
db.RemoveRange(files);
|
||||
await db.SaveChangesAsync();
|
||||
return count;
|
||||
}
|
||||
|
||||
public async Task<int> DeletePoolRecycledFilesAsync(Guid poolId)
|
||||
{
|
||||
var files = await db.Files
|
||||
.Where(f => f.PoolId == poolId && f.IsMarkedRecycle)
|
||||
.ToListAsync();
|
||||
var count = files.Count;
|
||||
var tasks = files.Select(DeleteFileDataAsync);
|
||||
await Task.WhenAll(tasks);
|
||||
var fileIds = files.Select(f => f.Id).ToList();
|
||||
await _PurgeCacheRangeAsync(fileIds);
|
||||
db.RemoveRange(files);
|
||||
await db.SaveChangesAsync();
|
||||
return count;
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAllRecycledFilesAsync()
|
||||
{
|
||||
var files = await db.Files
|
||||
.Where(f => f.IsMarkedRecycle)
|
||||
.ToListAsync();
|
||||
var count = files.Count;
|
||||
var tasks = files.Select(DeleteFileDataAsync);
|
||||
await Task.WhenAll(tasks);
|
||||
var fileIds = files.Select(f => f.Id).ToList();
|
||||
await _PurgeCacheRangeAsync(fileIds);
|
||||
db.RemoveRange(files);
|
||||
await db.SaveChangesAsync();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Reference in New Issue
Block a user