🐛 Try to fix the soft delete filter didn't work in drive
This commit is contained in:
@@ -48,27 +48,33 @@ public class AppDatabase(
|
|||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
// Apply soft-delete filter only to root entities, not derived types
|
// Apply soft-delete filter only to root entities, not derived types
|
||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
var entityTypes = modelBuilder.Model.GetEntityTypes();
|
||||||
|
foreach (var entityType in entityTypes)
|
||||||
{
|
{
|
||||||
if (!typeof(ModelBase).IsAssignableFrom(entityType.ClrType)) continue;
|
if (!typeof(ModelBase).IsAssignableFrom(entityType.ClrType)) continue;
|
||||||
|
|
||||||
// Skip derived types to avoid filter conflicts
|
// Skip derived types to avoid filter conflicts
|
||||||
var clrType = entityType.ClrType;
|
var clrType = entityType.ClrType;
|
||||||
if (clrType.BaseType != typeof(object) &&
|
if (clrType.BaseType != typeof(ModelBase) &&
|
||||||
typeof(ModelBase).IsAssignableFrom(clrType.BaseType))
|
typeof(ModelBase).IsAssignableFrom(clrType.BaseType))
|
||||||
{
|
{
|
||||||
continue; // Skip derived types
|
continue; // Skip derived types
|
||||||
}
|
}
|
||||||
|
|
||||||
var method = typeof(AppDatabase)
|
// Apply soft delete filter using cached reflection
|
||||||
.GetMethod(nameof(SetSoftDeleteFilter),
|
ApplySoftDeleteFilter(modelBuilder, clrType);
|
||||||
BindingFlags.NonPublic | BindingFlags.Static)!
|
|
||||||
.MakeGenericMethod(clrType);
|
|
||||||
|
|
||||||
method.Invoke(null, [modelBuilder]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly MethodInfo SetSoftDeleteFilterMethod = typeof(AppDatabase)
|
||||||
|
.GetMethod(nameof(SetSoftDeleteFilter), BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
|
||||||
|
private static void ApplySoftDeleteFilter(ModelBuilder modelBuilder, Type entityType)
|
||||||
|
{
|
||||||
|
var genericMethod = SetSoftDeleteFilterMethod.MakeGenericMethod(entityType);
|
||||||
|
genericMethod.Invoke(null, [modelBuilder]);
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetSoftDeleteFilter<TEntity>(ModelBuilder modelBuilder)
|
private static void SetSoftDeleteFilter<TEntity>(ModelBuilder modelBuilder)
|
||||||
where TEntity : ModelBase
|
where TEntity : ModelBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -157,7 +157,8 @@ public class FileIndexController(
|
|||||||
var query = db.Files
|
var query = db.Files
|
||||||
.Where(f => f.AccountId == accountId
|
.Where(f => f.AccountId == accountId
|
||||||
&& f.IsMarkedRecycle == recycled
|
&& f.IsMarkedRecycle == recycled
|
||||||
&& !db.FileIndexes.Any(fi => fi.FileId == f.Id && fi.AccountId == accountId))
|
&& !db.FileIndexes.Any(fi => fi.FileId == f.Id && fi.AccountId == accountId)
|
||||||
|
)
|
||||||
.OrderByDescending(f => f.CreatedAt)
|
.OrderByDescending(f => f.CreatedAt)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
@@ -509,4 +510,4 @@ public class CreateFileIndexRequest
|
|||||||
{
|
{
|
||||||
[MaxLength(32)] public string FileId { get; set; } = null!;
|
[MaxLength(32)] public string FileId { get; set; } = null!;
|
||||||
public string Path { get; set; } = null!;
|
public string Path { get; set; } = null!;
|
||||||
}
|
}
|
||||||
@@ -306,7 +306,7 @@ public class FileController(
|
|||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<ActionResult> DeleteFile(string id)
|
public async Task<ActionResult<SnCloudFile>> DeleteFile(string id)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||||
var userId = Guid.Parse(currentUser.Id);
|
var userId = Guid.Parse(currentUser.Id);
|
||||||
@@ -318,9 +318,9 @@ public class FileController(
|
|||||||
if (file is null) return NotFound();
|
if (file is null) return NotFound();
|
||||||
|
|
||||||
await fs.DeleteFileDataAsync(file, force: true);
|
await fs.DeleteFileDataAsync(file, force: true);
|
||||||
await fs.DeleteFileAsync(file);
|
await fs.DeleteFileAsync(file, skipData: true);
|
||||||
|
|
||||||
return NoContent();
|
return Ok(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ public class FileService(
|
|||||||
var bundle = await ValidateAndGetBundleAsync(fileBundleId, accountId);
|
var bundle = await ValidateAndGetBundleAsync(fileBundleId, accountId);
|
||||||
var finalExpiredAt = CalculateFinalExpiration(expiredAt, pool, bundle);
|
var finalExpiredAt = CalculateFinalExpiration(expiredAt, pool, bundle);
|
||||||
|
|
||||||
var (managedTempPath, fileSize, finalContentType) = await PrepareFileAsync(fileId, filePath, fileName, contentType);
|
var (managedTempPath, fileSize, finalContentType) =
|
||||||
|
await PrepareFileAsync(fileId, filePath, fileName, contentType);
|
||||||
|
|
||||||
var file = CreateFileObject(fileId, fileName, finalContentType, fileSize, finalExpiredAt, bundle, accountId);
|
var file = CreateFileObject(fileId, fileName, finalContentType, fileSize, finalExpiredAt, bundle, accountId);
|
||||||
|
|
||||||
@@ -112,7 +113,8 @@ public class FileService(
|
|||||||
await ExtractMetadataAsync(file, managedTempPath);
|
await ExtractMetadataAsync(file, managedTempPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var (processingPath, isTempFile) = await ProcessEncryptionAsync(fileId, managedTempPath, encryptPassword, pool, file);
|
var (processingPath, isTempFile) =
|
||||||
|
await ProcessEncryptionAsync(fileId, managedTempPath, encryptPassword, pool, file);
|
||||||
|
|
||||||
file.Hash = await HashFileAsync(processingPath);
|
file.Hash = await HashFileAsync(processingPath);
|
||||||
|
|
||||||
@@ -231,7 +233,8 @@ public class FileService(
|
|||||||
file.StorageId ??= file.Id;
|
file.StorageId ??= file.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PublishFileUploadedEventAsync(SnCloudFile file, FilePool pool, string processingPath, bool isTempFile)
|
private async Task PublishFileUploadedEventAsync(SnCloudFile file, FilePool pool, string processingPath,
|
||||||
|
bool isTempFile)
|
||||||
{
|
{
|
||||||
var js = nats.CreateJetStreamContext();
|
var js = nats.CreateJetStreamContext();
|
||||||
await js.PublishAsync(
|
await js.PublishAsync(
|
||||||
@@ -471,13 +474,14 @@ public class FileService(
|
|||||||
return await db.Files.AsNoTracking().FirstAsync(f => f.Id == file.Id);
|
return await db.Files.AsNoTracking().FirstAsync(f => f.Id == file.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteFileAsync(SnCloudFile file)
|
public async Task DeleteFileAsync(SnCloudFile file, bool skipData = false)
|
||||||
{
|
{
|
||||||
db.Remove(file);
|
db.Remove(file);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
await _PurgeCacheAsync(file.Id);
|
await _PurgeCacheAsync(file.Id);
|
||||||
|
|
||||||
await DeleteFileDataAsync(file);
|
if (!skipData)
|
||||||
|
await DeleteFileDataAsync(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteFileDataAsync(SnCloudFile file, bool force = false)
|
public async Task DeleteFileDataAsync(SnCloudFile file, bool force = false)
|
||||||
@@ -660,9 +664,12 @@ public class FileService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [.. references
|
return
|
||||||
.Select(r => cachedFiles.GetValueOrDefault(r.Id))
|
[
|
||||||
.Where(f => f != null)];
|
.. references
|
||||||
|
.Select(r => cachedFiles.GetValueOrDefault(r.Id))
|
||||||
|
.Where(f => f != null)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> GetReferenceCountAsync(string fileId)
|
public async Task<int> GetReferenceCountAsync(string fileId)
|
||||||
@@ -781,4 +788,4 @@ file class UpdatableCloudFile(SnCloudFile file)
|
|||||||
.SetProperty(f => f.UserMeta, userMeta)
|
.SetProperty(f => f.UserMeta, userMeta)
|
||||||
.SetProperty(f => f.IsMarkedRecycle, IsMarkedRecycle);
|
.SetProperty(f => f.IsMarkedRecycle, IsMarkedRecycle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,9 @@ public class SnCloudFile : ModelBase, ICloudFile, IIdentifiedResource
|
|||||||
/// Converts the CloudFile to a protobuf message
|
/// Converts the CloudFile to a protobuf message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The protobuf message representation of this object</returns>
|
/// <returns>The protobuf message representation of this object</returns>
|
||||||
public Proto.CloudFile ToProtoValue()
|
public CloudFile ToProtoValue()
|
||||||
{
|
{
|
||||||
var proto = new Proto.CloudFile
|
var proto = new CloudFile
|
||||||
{
|
{
|
||||||
Id = Id,
|
Id = Id,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
|
|||||||
Reference in New Issue
Block a user