♻️ Extract the Storage service to DysonNetwork.Drive microservice
This commit is contained in:
		
							
								
								
									
										123
									
								
								DysonNetwork.Drive/FileController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								DysonNetwork.Drive/FileController.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Minio.DataModel.Args; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| // Using fully qualified names to avoid ambiguity with DysonNetwork.Common.Models | ||||
| using DysonNetwork.Drive.Attributes; | ||||
| using DysonNetwork.Drive.Models; | ||||
|  | ||||
| namespace DysonNetwork.Drive; | ||||
|  | ||||
| [ApiController] | ||||
| [Route("/files")] | ||||
| public class FileController( | ||||
|     AppDatabase db, | ||||
|     FileService fs, | ||||
|     IConfiguration configuration, | ||||
|     IWebHostEnvironment env, | ||||
|     FileReferenceMigrationService rms | ||||
| ) : ControllerBase | ||||
| { | ||||
|     [HttpGet("{id}")] | ||||
|     public async Task<ActionResult> OpenFile(string id, [FromQuery] bool original = false) | ||||
|     { | ||||
|         var file = await fs.GetFileAsync(id); | ||||
|         if (file is null) return NotFound(); | ||||
|          | ||||
|         if (!string.IsNullOrWhiteSpace(file.StorageUrl)) return Redirect(file.StorageUrl); | ||||
|          | ||||
|         if (file.UploadedTo is null) | ||||
|         { | ||||
|             var tusStorePath = configuration.GetValue<string>("Tus:StorePath")!; | ||||
|             var filePath = Path.Combine(env.ContentRootPath, tusStorePath, file.Id); | ||||
|             if (!System.IO.File.Exists(filePath)) return new NotFoundResult(); | ||||
|             return PhysicalFile(filePath, file.MimeType ?? "application/octet-stream", file.Name); | ||||
|         } | ||||
|  | ||||
|         var dest = fs.GetRemoteStorageConfig(file.UploadedTo); | ||||
|         var fileName = string.IsNullOrWhiteSpace(file.StorageId) ? file.Id : file.StorageId; | ||||
|  | ||||
|         if (!original && file.HasCompression) | ||||
|             fileName += ".compressed"; | ||||
|  | ||||
|         if (dest.ImageProxy is not null && (file.MimeType?.StartsWith("image/") ?? false)) | ||||
|         { | ||||
|             var proxyUrl = dest.ImageProxy; | ||||
|             var baseUri = new Uri(proxyUrl.EndsWith('/') ? proxyUrl : $"{proxyUrl}/"); | ||||
|             var fullUri = new Uri(baseUri, fileName); | ||||
|             return Redirect(fullUri.ToString()); | ||||
|         } | ||||
|  | ||||
|         if (dest.AccessProxy is not null) | ||||
|         { | ||||
|             var proxyUrl = dest.AccessProxy; | ||||
|             var baseUri = new Uri(proxyUrl.EndsWith('/') ? proxyUrl : $"{proxyUrl}/"); | ||||
|             var fullUri = new Uri(baseUri, fileName); | ||||
|             return Redirect(fullUri.ToString()); | ||||
|         } | ||||
|  | ||||
|         if (dest.EnableSigned) | ||||
|         { | ||||
|             var client = fs.CreateMinioClient(dest); | ||||
|             if (client is null) | ||||
|                 return BadRequest( | ||||
|                     "Failed to configure client for remote destination, file got an invalid storage remote."); | ||||
|  | ||||
|             var bucket = dest.Bucket; | ||||
|             var openUrl = await client.PresignedGetObjectAsync( | ||||
|                 new PresignedGetObjectArgs() | ||||
|                     .WithBucket(bucket) | ||||
|                     .WithObject(fileName) | ||||
|                     .WithExpiry(3600) | ||||
|             ); | ||||
|  | ||||
|             return Redirect(openUrl); | ||||
|         } | ||||
|  | ||||
|         // Fallback redirect to the S3 endpoint (public read) | ||||
|         var protocol = dest.EnableSsl ? "https" : "http"; | ||||
|         // Use the path bucket lookup mode | ||||
|         return Redirect($"{protocol}://{dest.Endpoint}/{dest.Bucket}/{fileName}"); | ||||
|     } | ||||
|  | ||||
|     [HttpGet("{id}/info")] | ||||
|     public async Task<ActionResult<Models.CloudFile>> GetFileInfo(string id) | ||||
|     { | ||||
|         var file = await db.Files.FindAsync(id); | ||||
|         if (file is null) return NotFound(); | ||||
|  | ||||
|         return file; | ||||
|     } | ||||
|  | ||||
|     [Authorize] | ||||
|     [HttpDelete("{id}")] | ||||
|     public async Task<ActionResult> DeleteFile(string id) | ||||
|     { | ||||
|         if (HttpContext.Items["CurrentUser"] is not Models.Account currentUser) return Unauthorized(); | ||||
|         var userId = currentUser.Id; | ||||
|  | ||||
|         var file = await db.Files | ||||
|             .Where(e => e.Id == id) | ||||
|             .Where(e => e.Account.Id == userId) | ||||
|             .FirstOrDefaultAsync(); | ||||
|         if (file is null) return NotFound(); | ||||
|  | ||||
|         await fs.DeleteFileAsync(file); | ||||
|  | ||||
|         db.Files.Remove(file); | ||||
|         await db.SaveChangesAsync(); | ||||
|  | ||||
|         return NoContent(); | ||||
|     } | ||||
|      | ||||
|     [HttpPost("/maintenance/migrateReferences")] | ||||
|     [Authorize] | ||||
|     [RequiredPermission("maintenance.files.references")] | ||||
|     public async Task<ActionResult> MigrateFileReferences() | ||||
|     { | ||||
|         await rms.ScanAndMigrateReferences(); | ||||
|         return Ok(); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user