diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index cde67e9..94999be 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -304,68 +304,6 @@ app.MapControllers().RequireRateLimiting("fixed"); app.MapStaticAssets().RequireRateLimiting("fixed"); app.MapRazorPages().RequireRateLimiting("fixed"); -app.MapTus("/files/tus", _ => Task.FromResult(new() -{ - Store = tusDiskStore, - Events = new Events - { - OnAuthorizeAsync = async eventContext => - { - if (eventContext.Intent == IntentType.DeleteFile) - { - eventContext.FailRequest( - HttpStatusCode.BadRequest, - "Deleting files from this endpoint was disabled, please refer to the Dyson Network File API." - ); - return; - } +app.MapTus("/files/tus", _ => Task.FromResult(TusService.BuildConfiguration(tusDiskStore))); - var httpContext = eventContext.HttpContext; - if (httpContext.Items["CurrentUser"] is not Account user) - { - eventContext.FailRequest(HttpStatusCode.Unauthorized); - return; - } - - if (!user.IsSuperuser) - { - using var scope = httpContext.RequestServices.CreateScope(); - var pm = scope.ServiceProvider.GetRequiredService(); - var allowed = await pm.HasPermissionAsync($"user:{user.Id}", "global", "files.create"); - if (!allowed) - eventContext.FailRequest(HttpStatusCode.Forbidden); - } - }, - OnFileCompleteAsync = async eventContext => - { - using var scope = eventContext.HttpContext.RequestServices.CreateScope(); - var services = scope.ServiceProvider; - - var httpContext = eventContext.HttpContext; - if (httpContext.Items["CurrentUser"] is not Account user) return; - - var file = await eventContext.GetFileAsync(); - var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); - var fileName = metadata.TryGetValue("filename", out var fn) - ? fn.GetString(Encoding.UTF8) - : "uploaded_file"; - var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; - - var fileStream = await file.GetContentAsync(eventContext.CancellationToken); - - var fileService = services.GetRequiredService(); - var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); - - using var finalScope = eventContext.HttpContext.RequestServices.CreateScope(); - var jsonOptions = finalScope.ServiceProvider.GetRequiredService>().Value - .JsonSerializerOptions; - var infoJson = JsonSerializer.Serialize(info, jsonOptions); - eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); - - // Dispose the stream after all processing is complete - await fileStream.DisposeAsync(); - } - } -})); - -app.Run(); +app.Run(); \ No newline at end of file diff --git a/DysonNetwork.Sphere/Storage/TusService.cs b/DysonNetwork.Sphere/Storage/TusService.cs new file mode 100644 index 0000000..c66ea19 --- /dev/null +++ b/DysonNetwork.Sphere/Storage/TusService.cs @@ -0,0 +1,78 @@ +using System.Net; +using System.Text; +using System.Text.Json; +using DysonNetwork.Sphere.Permission; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using tusdotnet.Interfaces; +using tusdotnet.Models; +using tusdotnet.Models.Configuration; + +namespace DysonNetwork.Sphere.Storage; + +public class TusService +{ + public static DefaultTusConfiguration BuildConfiguration(ITusStore store) => new() + { + Store = store, + Events = new Events + { + OnAuthorizeAsync = async eventContext => + { + if (eventContext.Intent == IntentType.DeleteFile) + { + eventContext.FailRequest( + HttpStatusCode.BadRequest, + "Deleting files from this endpoint was disabled, please refer to the Dyson Network File API." + ); + return; + } + + var httpContext = eventContext.HttpContext; + if (httpContext.Items["CurrentUser"] is not Account.Account user) + { + eventContext.FailRequest(HttpStatusCode.Unauthorized); + return; + } + + if (!user.IsSuperuser) + { + using var scope = httpContext.RequestServices.CreateScope(); + var pm = scope.ServiceProvider.GetRequiredService(); + var allowed = await pm.HasPermissionAsync($"user:{user.Id}", "global", "files.create"); + if (!allowed) + eventContext.FailRequest(HttpStatusCode.Forbidden); + } + }, + OnFileCompleteAsync = async eventContext => + { + using var scope = eventContext.HttpContext.RequestServices.CreateScope(); + var services = scope.ServiceProvider; + + var httpContext = eventContext.HttpContext; + if (httpContext.Items["CurrentUser"] is not Account.Account user) return; + + var file = await eventContext.GetFileAsync(); + var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); + var fileName = metadata.TryGetValue("filename", out var fn) + ? fn.GetString(Encoding.UTF8) + : "uploaded_file"; + var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; + + var fileStream = await file.GetContentAsync(eventContext.CancellationToken); + + var fileService = services.GetRequiredService(); + var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); + + using var finalScope = eventContext.HttpContext.RequestServices.CreateScope(); + var jsonOptions = finalScope.ServiceProvider.GetRequiredService>().Value + .JsonSerializerOptions; + var infoJson = JsonSerializer.Serialize(info, jsonOptions); + eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); + + // Dispose the stream after all processing is complete + await fileStream.DisposeAsync(); + } + } + }; +} \ No newline at end of file