From 34e78294a119df9c42e048da57e2479246ef2323 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 17 Nov 2025 22:20:49 +0800 Subject: [PATCH] :sparkles: Unindexed files has similar filter to the list file API --- .../Index/FileIndexController.cs | 70 +++++++++++-------- DysonNetwork.Drive/Storage/FileController.cs | 45 ++++++------ 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/DysonNetwork.Drive/Index/FileIndexController.cs b/DysonNetwork.Drive/Index/FileIndexController.cs index 65736b8..77f0ffc 100644 --- a/DysonNetwork.Drive/Index/FileIndexController.cs +++ b/DysonNetwork.Drive/Index/FileIndexController.cs @@ -31,17 +31,17 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { var fileIndexes = await fileIndexService.GetByPathAsync(accountId, path); - + // Get all file indexes for this account to extract child folders var allFileIndexes = await fileIndexService.GetByAccountIdAsync(accountId); - + // Extract unique child folder paths var childFolders = ExtractChildFolders(allFileIndexes, path); - + return Ok(new { Path = path, @@ -76,14 +76,14 @@ public class FileIndexController( foreach (var index in allFileIndexes) { var normalizedIndexPath = FileIndexService.NormalizePath(index.Path); - + // Check if this path is a direct child of the parent path - if (normalizedIndexPath.StartsWith(normalizedParentPath) && + if (normalizedIndexPath.StartsWith(normalizedParentPath) && normalizedIndexPath != normalizedParentPath) { // Remove the parent path prefix to get the relative path var relativePath = normalizedIndexPath.Substring(normalizedParentPath.Length); - + // Extract the first folder name (direct child) var firstSlashIndex = relativePath.IndexOf('/'); if (firstSlashIndex > 0) @@ -108,11 +108,11 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { var fileIndexes = await fileIndexService.GetByAccountIdAsync(accountId); - + return Ok(new { Files = fileIndexes, @@ -134,11 +134,18 @@ public class FileIndexController( /// /// Gets files that have not been indexed for the current user. /// + /// Shows recycled files or not /// The number of files to skip /// The number of files to return + /// The pool ID of those files /// List of unindexed files [HttpGet("unindexed")] - public async Task GetUnindexedFiles([FromQuery] int offset = 0, [FromQuery] int take = 20) + public async Task GetUnindexedFiles( + [FromQuery] Guid? pool, + [FromQuery] bool recycled = false, + [FromQuery] int offset = 0, + [FromQuery] int take = 20 + ) { if (HttpContext.Items["CurrentUser"] is not Account currentUser) return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; @@ -149,14 +156,17 @@ public class FileIndexController( { var query = db.Files .Where(f => f.AccountId == accountId - && !f.IsMarkedRecycle + && f.IsMarkedRecycle == recycled && !db.FileIndexes.Any(fi => fi.FileId == f.Id && fi.AccountId == accountId)) - .OrderByDescending(f => f.CreatedAt); + .OrderByDescending(f => f.CreatedAt) + .AsQueryable(); + + if (pool.HasValue) query = query.Where(f => f.PoolId == pool); var totalCount = await query.CountAsync(); - + Response.Headers.Append("X-Total", totalCount.ToString()); - + var unindexedFiles = await query .Skip(offset) .Take(take) @@ -189,7 +199,7 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { // Verify ownership @@ -201,7 +211,7 @@ public class FileIndexController( return new ObjectResult(ApiError.NotFound("File index")) { StatusCode = 404 }; var updatedIndex = await fileIndexService.UpdateAsync(indexId, request.NewPath); - + if (updatedIndex == null) return new ObjectResult(ApiError.NotFound("File index")) { StatusCode = 404 }; @@ -239,7 +249,7 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { // Verify ownership @@ -256,7 +266,7 @@ public class FileIndexController( // Remove the index var removed = await fileIndexService.RemoveAsync(indexId); - + if (!removed) return new ObjectResult(ApiError.NotFound("File index")) { StatusCode = 404 }; @@ -296,7 +306,9 @@ public class FileIndexController( return Ok(new { - Message = deleteFile ? "File index and file data removed successfully" : "File index removed successfully", + Message = deleteFile + ? "File index and file data removed successfully" + : "File index removed successfully", FileId = fileId, FileName = fileName, Path = filePath, @@ -328,7 +340,7 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { var removedCount = await fileIndexService.RemoveByPathAsync(accountId, path); @@ -357,13 +369,14 @@ public class FileIndexController( db.Files.Remove(file); logger.LogInformation("Deleted orphaned file {FileId} after clearing path {Path}", fileId, path); } + await db.SaveChangesAsync(); return Ok(new { - Message = deleteFiles ? - $"Cleared {removedCount} file indexes from path and deleted orphaned files" : - $"Cleared {removedCount} file indexes from path", + Message = deleteFiles + ? $"Cleared {removedCount} file indexes from path and deleted orphaned files" + : $"Cleared {removedCount} file indexes from path", Path = path, RemovedCount = removedCount, FilesDeleted = deleteFiles @@ -393,7 +406,7 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { // Verify the file exists and belongs to the user @@ -406,7 +419,8 @@ public class FileIndexController( // Check if index already exists for this file and path var existingIndex = await db.FileIndexes - .FirstOrDefaultAsync(fi => fi.FileId == request.FileId && fi.Path == request.Path && fi.AccountId == accountId); + .FirstOrDefaultAsync(fi => + fi.FileId == request.FileId && fi.Path == request.Path && fi.AccountId == accountId); if (existingIndex != null) return new ObjectResult(ApiError.Validation(new Dictionary @@ -426,7 +440,7 @@ public class FileIndexController( } catch (Exception ex) { - logger.LogError(ex, "Failed to create file index for file {FileId} at path {Path} for account {AccountId}", + logger.LogError(ex, "Failed to create file index for file {FileId} at path {Path} for account {AccountId}", request.FileId, request.Path, accountId); return new ObjectResult(new ApiError { @@ -450,7 +464,7 @@ public class FileIndexController( return new ObjectResult(ApiError.Unauthorized()) { StatusCode = 401 }; var accountId = Guid.Parse(currentUser.Id); - + try { // Build the query with all conditions at once @@ -458,7 +472,7 @@ public class FileIndexController( var fileIndexes = await db.FileIndexes .Where(fi => fi.AccountId == accountId) .Include(fi => fi.File) - .Where(fi => + .Where(fi => (string.IsNullOrEmpty(path) || fi.Path == FileIndexService.NormalizePath(path)) && (fi.File.Name.ToLower().Contains(searchTerm) || (fi.File.Description != null && fi.File.Description.ToLower().Contains(searchTerm)) || diff --git a/DysonNetwork.Drive/Storage/FileController.cs b/DysonNetwork.Drive/Storage/FileController.cs index 812fbf6..d8849d2 100644 --- a/DysonNetwork.Drive/Storage/FileController.cs +++ b/DysonNetwork.Drive/Storage/FileController.cs @@ -63,30 +63,31 @@ public class FileController( return null; } - private async Task ServeLocalFile(SnCloudFile file) + private Task ServeLocalFile(SnCloudFile file) { // Try temp storage first var tempFilePath = Path.Combine(Path.GetTempPath(), file.Id); if (System.IO.File.Exists(tempFilePath)) { if (file.IsEncrypted) - return StatusCode(StatusCodes.Status403Forbidden, "Encrypted files cannot be accessed before they are processed and stored."); + return Task.FromResult(StatusCode(StatusCodes.Status403Forbidden, + "Encrypted files cannot be accessed before they are processed and stored.")); - return PhysicalFile(tempFilePath, file.MimeType ?? "application/octet-stream", file.Name, enableRangeProcessing: true); + return Task.FromResult(PhysicalFile(tempFilePath, file.MimeType ?? "application/octet-stream", + file.Name, enableRangeProcessing: true)); } // Fallback for tus uploads var tusStorePath = configuration.GetValue("Storage:Uploads"); - if (!string.IsNullOrEmpty(tusStorePath)) - { - var tusFilePath = Path.Combine(env.ContentRootPath, tusStorePath, file.Id); - if (System.IO.File.Exists(tusFilePath)) - { - return PhysicalFile(tusFilePath, file.MimeType ?? "application/octet-stream", file.Name, enableRangeProcessing: true); - } - } - - return StatusCode(StatusCodes.Status400BadRequest, "File is being processed. Please try again later."); + if (string.IsNullOrEmpty(tusStorePath)) + return Task.FromResult(StatusCode(StatusCodes.Status400BadRequest, + "File is being processed. Please try again later.")); + var tusFilePath = Path.Combine(env.ContentRootPath, tusStorePath, file.Id); + return System.IO.File.Exists(tusFilePath) + ? Task.FromResult(PhysicalFile(tusFilePath, file.MimeType ?? "application/octet-stream", + file.Name, enableRangeProcessing: true)) + : Task.FromResult(StatusCode(StatusCodes.Status400BadRequest, + "File is being processed. Please try again later.")); } private async Task ServeRemoteFile( @@ -99,7 +100,8 @@ public class FileController( ) { if (!file.PoolId.HasValue) - return StatusCode(StatusCodes.Status500InternalServerError, "File is in an inconsistent state: uploaded but no pool ID."); + return StatusCode(StatusCodes.Status500InternalServerError, + "File is in an inconsistent state: uploaded but no pool ID."); var pool = await fs.GetPoolAsync(file.PoolId.Value); if (pool is null) @@ -141,22 +143,17 @@ public class FileController( return fileName; } - private ActionResult? TryProxyRedirect(SnCloudFile file, RemoteStorageConfig dest, string fileName) + public ActionResult? TryProxyRedirect(SnCloudFile file, RemoteStorageConfig dest, string fileName) { if (dest.ImageProxy is not null && (file.MimeType?.StartsWith("image/") ?? false)) { return Redirect(BuildProxyUrl(dest.ImageProxy, fileName)); } - if (dest.AccessProxy is not null) - { - return Redirect(BuildProxyUrl(dest.AccessProxy, fileName)); - } - - return null; + return dest.AccessProxy is not null ? Redirect(BuildProxyUrl(dest.AccessProxy, fileName)) : null; } - private string BuildProxyUrl(string proxyUrl, string fileName) + private static string BuildProxyUrl(string proxyUrl, string fileName) { var baseUri = new Uri(proxyUrl.EndsWith('/') ? proxyUrl : $"{proxyUrl}/"); var fullUri = new Uri(baseUri, fileName); @@ -189,7 +186,7 @@ public class FileController( return Redirect(openUrl); } - private Dictionary BuildSignedUrlHeaders( + private static Dictionary BuildSignedUrlHeaders( SnCloudFile file, string? fileExtension, string? overrideMimeType, @@ -451,4 +448,4 @@ public class FileController( throw; } } -} +} \ No newline at end of file