diff --git a/DysonNetwork.Drive/AppDatabase.cs b/DysonNetwork.Drive/AppDatabase.cs index da5176d..2bb530d 100644 --- a/DysonNetwork.Drive/AppDatabase.cs +++ b/DysonNetwork.Drive/AppDatabase.cs @@ -48,27 +48,33 @@ public class AppDatabase( base.OnModelCreating(modelBuilder); // 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; // Skip derived types to avoid filter conflicts var clrType = entityType.ClrType; - if (clrType.BaseType != typeof(object) && + if (clrType.BaseType != typeof(ModelBase) && typeof(ModelBase).IsAssignableFrom(clrType.BaseType)) { continue; // Skip derived types } - var method = typeof(AppDatabase) - .GetMethod(nameof(SetSoftDeleteFilter), - BindingFlags.NonPublic | BindingFlags.Static)! - .MakeGenericMethod(clrType); - - method.Invoke(null, [modelBuilder]); + // Apply soft delete filter using cached reflection + ApplySoftDeleteFilter(modelBuilder, clrType); } } + 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(ModelBuilder modelBuilder) where TEntity : ModelBase { diff --git a/DysonNetwork.Drive/Index/FileIndexController.cs b/DysonNetwork.Drive/Index/FileIndexController.cs index 77f0ffc..06b6e73 100644 --- a/DysonNetwork.Drive/Index/FileIndexController.cs +++ b/DysonNetwork.Drive/Index/FileIndexController.cs @@ -157,7 +157,8 @@ public class FileIndexController( var query = db.Files .Where(f => f.AccountId == accountId && 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) .AsQueryable(); @@ -509,4 +510,4 @@ public class CreateFileIndexRequest { [MaxLength(32)] public string FileId { get; set; } = null!; public string Path { get; set; } = null!; -} +} \ No newline at end of file diff --git a/DysonNetwork.Drive/Storage/FileController.cs b/DysonNetwork.Drive/Storage/FileController.cs index 3751410..be129b2 100644 --- a/DysonNetwork.Drive/Storage/FileController.cs +++ b/DysonNetwork.Drive/Storage/FileController.cs @@ -306,7 +306,7 @@ public class FileController( [Authorize] [HttpDelete("{id}")] - public async Task DeleteFile(string id) + public async Task> DeleteFile(string id) { if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); var userId = Guid.Parse(currentUser.Id); @@ -318,9 +318,9 @@ public class FileController( if (file is null) return NotFound(); await fs.DeleteFileDataAsync(file, force: true); - await fs.DeleteFileAsync(file); + await fs.DeleteFileAsync(file, skipData: true); - return NoContent(); + return Ok(file); } [Authorize] diff --git a/DysonNetwork.Drive/Storage/FileService.cs b/DysonNetwork.Drive/Storage/FileService.cs index b9acb51..d94a403 100644 --- a/DysonNetwork.Drive/Storage/FileService.cs +++ b/DysonNetwork.Drive/Storage/FileService.cs @@ -103,7 +103,8 @@ public class FileService( var bundle = await ValidateAndGetBundleAsync(fileBundleId, accountId); 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); @@ -112,7 +113,8 @@ public class FileService( 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); @@ -231,7 +233,8 @@ public class FileService( 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(); await js.PublishAsync( @@ -471,13 +474,14 @@ public class FileService( 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); await db.SaveChangesAsync(); await _PurgeCacheAsync(file.Id); - await DeleteFileDataAsync(file); + if (!skipData) + await DeleteFileDataAsync(file); } public async Task DeleteFileDataAsync(SnCloudFile file, bool force = false) @@ -660,9 +664,12 @@ public class FileService( } } - return [.. references - .Select(r => cachedFiles.GetValueOrDefault(r.Id)) - .Where(f => f != null)]; + return + [ + .. references + .Select(r => cachedFiles.GetValueOrDefault(r.Id)) + .Where(f => f != null) + ]; } public async Task GetReferenceCountAsync(string fileId) @@ -781,4 +788,4 @@ file class UpdatableCloudFile(SnCloudFile file) .SetProperty(f => f.UserMeta, userMeta) .SetProperty(f => f.IsMarkedRecycle, IsMarkedRecycle); } -} +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Models/CloudFile.cs b/DysonNetwork.Shared/Models/CloudFile.cs index b3f8971..46256dc 100644 --- a/DysonNetwork.Shared/Models/CloudFile.cs +++ b/DysonNetwork.Shared/Models/CloudFile.cs @@ -86,9 +86,9 @@ public class SnCloudFile : ModelBase, ICloudFile, IIdentifiedResource /// Converts the CloudFile to a protobuf message /// /// The protobuf message representation of this object - public Proto.CloudFile ToProtoValue() + public CloudFile ToProtoValue() { - var proto = new Proto.CloudFile + var proto = new CloudFile { Id = Id, Name = Name,