From f300bbc197174ae19e55c03f4be5825f1711cad5 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 16 Feb 2022 16:33:00 +0000 Subject: [PATCH] Refactor --- VoidCat/Controllers/Admin/AdminController.cs | 2 + VoidCat/Controllers/DownloadController.cs | 1 + VoidCat/Controllers/StatsController.cs | 14 ++- VoidCat/Controllers/UploadController.cs | 20 ++-- VoidCat/Model/VoidFile.cs | 8 +- VoidCat/Model/VoidSettings.cs | 19 +--- VoidCat/Program.cs | 21 ++++- .../Abstractions/IFileMetadataStore.cs | 12 +++ VoidCat/Services/Abstractions/IFileStore.cs | 46 +++++++++ .../Services/Abstractions/IStatsCollector.cs | 12 +++ VoidCat/Services/Abstractions/IUserManager.cs | 10 ++ VoidCat/Services/IFileMetadataStore.cs | 12 --- VoidCat/Services/IFileStore.cs | 48 ---------- VoidCat/Services/IStatsCollector.cs | 13 --- VoidCat/Services/InMemoryStatsCollector.cs | 94 +++++++++---------- .../Services/LocalDiskFileMetadataStore.cs | 11 ++- VoidCat/Services/LocalDiskFileStorage.cs | 28 +++--- VoidCat/Services/PrometheusStatsCollector.cs | 5 +- VoidCat/VoidCat.csproj | 1 + VoidCat/spa/src/FileUpload.js | 13 +-- VoidCat/spa/src/GlobalStats.css | 2 +- VoidCat/spa/src/GlobalStats.js | 2 + 22 files changed, 213 insertions(+), 181 deletions(-) create mode 100644 VoidCat/Services/Abstractions/IFileMetadataStore.cs create mode 100644 VoidCat/Services/Abstractions/IFileStore.cs create mode 100644 VoidCat/Services/Abstractions/IStatsCollector.cs create mode 100644 VoidCat/Services/Abstractions/IUserManager.cs delete mode 100644 VoidCat/Services/IFileMetadataStore.cs delete mode 100644 VoidCat/Services/IFileStore.cs delete mode 100644 VoidCat/Services/IStatsCollector.cs diff --git a/VoidCat/Controllers/Admin/AdminController.cs b/VoidCat/Controllers/Admin/AdminController.cs index a211f95..303af7a 100644 --- a/VoidCat/Controllers/Admin/AdminController.cs +++ b/VoidCat/Controllers/Admin/AdminController.cs @@ -1,8 +1,10 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace VoidCat.Controllers.Admin; [Route("admin")] +[Authorize(Policy = "Admin")] public class AdminController : Controller { diff --git a/VoidCat/Controllers/DownloadController.cs b/VoidCat/Controllers/DownloadController.cs index f5edcca..2a1bac2 100644 --- a/VoidCat/Controllers/DownloadController.cs +++ b/VoidCat/Controllers/DownloadController.cs @@ -2,6 +2,7 @@ using System.Net; using Microsoft.AspNetCore.Mvc; using VoidCat.Model; using VoidCat.Services; +using VoidCat.Services.Abstractions; namespace VoidCat.Controllers; diff --git a/VoidCat/Controllers/StatsController.cs b/VoidCat/Controllers/StatsController.cs index 307bf39..d982155 100644 --- a/VoidCat/Controllers/StatsController.cs +++ b/VoidCat/Controllers/StatsController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using VoidCat.Model; using VoidCat.Services; +using VoidCat.Services.Abstractions; namespace VoidCat.Controllers { @@ -8,17 +9,24 @@ namespace VoidCat.Controllers public class StatsController : Controller { private readonly IStatsCollector _statsCollector; + private readonly IFileStore _fileStore; - public StatsController(IStatsCollector statsCollector) + public StatsController(IStatsCollector statsCollector, IFileStore fileStore) { _statsCollector = statsCollector; + _fileStore = fileStore; } [HttpGet] public async Task GetGlobalStats() { var bw = await _statsCollector.GetBandwidth(); - return new(bw); + var bytes = 0UL; + await foreach (var vf in _fileStore.ListFiles()) + { + bytes += vf.Size; + } + return new(bw, bytes); } [HttpGet] @@ -30,6 +38,6 @@ namespace VoidCat.Controllers } } - public sealed record GlobalStats(Bandwidth Bandwidth); + public sealed record GlobalStats(Bandwidth Bandwidth, ulong TotalBytes); public sealed record FileStats(Bandwidth Bandwidth); } diff --git a/VoidCat/Controllers/UploadController.cs b/VoidCat/Controllers/UploadController.cs index e02346e..c3c83a9 100644 --- a/VoidCat/Controllers/UploadController.cs +++ b/VoidCat/Controllers/UploadController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Newtonsoft.Json; using VoidCat.Model; using VoidCat.Services; +using VoidCat.Services.Abstractions; namespace VoidCat.Controllers { @@ -31,8 +32,7 @@ namespace VoidCat.Controllers }; var digest = Request.Headers.GetHeader("X-Digest"); - var vf = await (Request.HasFormContentType ? - saveFromForm() : _storage.Ingress(new(Request.Body, meta, digest!), HttpContext.RequestAborted)); + var vf = await _storage.Ingress(new(Request.Body, meta, digest!), HttpContext.RequestAborted); return UploadResult.Success(vf); } @@ -52,7 +52,7 @@ namespace VoidCat.Controllers { var gid = id.FromBase58Guid(); var fileInfo = await _storage.Get(gid); - if (fileInfo == default) return null; + if (fileInfo == default) return UploadResult.Error("File not found"); var editSecret = Request.Headers.GetHeader("X-EditSecret"); var digest = Request.Headers.GetHeader("X-Digest"); @@ -72,14 +72,14 @@ namespace VoidCat.Controllers [HttpGet] [Route("{id}")] - public Task GetInfo([FromRoute] string id) + public ValueTask GetInfo([FromRoute] string id) { return _storage.Get(id.FromBase58Guid()); } [HttpPatch] [Route("{id}")] - public Task UpdateFileInfo([FromRoute] string id, [FromBody] UpdateFileInfoRequest request) + public ValueTask UpdateFileInfo([FromRoute] string id, [FromBody] UpdateFileInfoRequest request) { return _storage.UpdateInfo(new VoidFile() { @@ -88,12 +88,8 @@ namespace VoidCat.Controllers }, request.EditSecret); } - private Task saveFromForm() - { - return Task.FromResult(null); - } - - public record UpdateFileInfoRequest([JsonConverter(typeof(Base58GuidConverter))] Guid EditSecret, VoidFileMeta Metadata); + public record UpdateFileInfoRequest([JsonConverter(typeof(Base58GuidConverter))] Guid EditSecret, + VoidFileMeta Metadata); } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] @@ -120,4 +116,4 @@ namespace VoidCat.Controllers public static UploadResult Error(string message) => new(false, null, message); } -} +} \ No newline at end of file diff --git a/VoidCat/Model/VoidFile.cs b/VoidCat/Model/VoidFile.cs index 827925c..b07c8f0 100644 --- a/VoidCat/Model/VoidFile.cs +++ b/VoidCat/Model/VoidFile.cs @@ -3,25 +3,25 @@ using Newtonsoft.Json; namespace VoidCat.Model { - public record class VoidFile + public record VoidFile { [JsonConverter(typeof(Base58GuidConverter))] public Guid Id { get; init; } - public VoidFileMeta Metadata { get; set; } + public VoidFileMeta? Metadata { get; set; } public ulong Size { get; init; } public DateTimeOffset Uploaded { get; init; } } - public record class InternalVoidFile : VoidFile + public record InternalVoidFile : VoidFile { [JsonConverter(typeof(Base58GuidConverter))] public Guid EditSecret { get; init; } } - public record class VoidFileMeta + public record VoidFileMeta { public string? Name { get; init; } diff --git a/VoidCat/Model/VoidSettings.cs b/VoidCat/Model/VoidSettings.cs index ee81a85..5e55afc 100644 --- a/VoidCat/Model/VoidSettings.cs +++ b/VoidCat/Model/VoidSettings.cs @@ -5,20 +5,11 @@ public string DataDirectory { get; init; } = "./data"; public TorSettings? TorSettings { get; init; } + + public JwtSettings JwtSettings { get; init; } = new("void_cat_internal", "default_key"); } - public class TorSettings - { - public TorSettings(Uri torControl, string privateKey, string controlPassword) - { - TorControl = torControl; - PrivateKey = privateKey; - ControlPassword = controlPassword; - } - - public Uri TorControl { get; } - public string PrivateKey { get; } - - public string ControlPassword { get; } - } + public sealed record TorSettings(Uri TorControl, string PrivateKey, string ControlPassword); + + public sealed record JwtSettings(string Issuer, string Key); } diff --git a/VoidCat/Program.cs b/VoidCat/Program.cs index ce951b7..8502d27 100644 --- a/VoidCat/Program.cs +++ b/VoidCat/Program.cs @@ -1,6 +1,10 @@ +using System.Text; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; using Prometheus; using VoidCat.Model; using VoidCat.Services; +using VoidCat.Services.Abstractions; var builder = WebApplication.CreateBuilder(args); var services = builder.Services; @@ -14,17 +18,32 @@ builder.Logging.AddSeq(seqSettings); services.AddRouting(); services.AddControllers().AddNewtonsoftJson(); +services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new() + { + ValidateIssuer = true, + ValidateAudience = false, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = voidSettings.JwtSettings.Issuer, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(voidSettings.JwtSettings.Key)) + }; + }); services.AddMemoryCache(); services.AddScoped(); -services.AddScoped(); +services.AddScoped(); services.AddScoped(); var app = builder.Build(); app.UseStaticFiles(); +app.UseAuthentication(); app.UseRouting(); +app.UseAuthorization(); app.UseEndpoints(ep => { ep.MapControllers(); diff --git a/VoidCat/Services/Abstractions/IFileMetadataStore.cs b/VoidCat/Services/Abstractions/IFileMetadataStore.cs new file mode 100644 index 0000000..3d68610 --- /dev/null +++ b/VoidCat/Services/Abstractions/IFileMetadataStore.cs @@ -0,0 +1,12 @@ +using VoidCat.Model; + +namespace VoidCat.Services.Abstractions; + +public interface IFileMetadataStore +{ + ValueTask Get(Guid id); + + ValueTask Set(InternalVoidFile meta); + + ValueTask Update(VoidFile patch, Guid editSecret); +} diff --git a/VoidCat/Services/Abstractions/IFileStore.cs b/VoidCat/Services/Abstractions/IFileStore.cs new file mode 100644 index 0000000..a8ab3eb --- /dev/null +++ b/VoidCat/Services/Abstractions/IFileStore.cs @@ -0,0 +1,46 @@ +using VoidCat.Model; + +namespace VoidCat.Services.Abstractions; + +public interface IFileStore +{ + ValueTask Get(Guid id); + + ValueTask Ingress(IngressPayload payload, CancellationToken cts); + + ValueTask Egress(EgressRequest request, Stream outStream, CancellationToken cts); + + ValueTask UpdateInfo(VoidFile patch, Guid editSecret); + + IAsyncEnumerable ListFiles(); +} + +public sealed record IngressPayload(Stream InStream, VoidFileMeta Meta, string Hash) +{ + public Guid? Id { get; init; } + public Guid? EditSecret { get; init; } + + public bool IsAppend => Id.HasValue && EditSecret.HasValue; +} + +public sealed record EgressRequest(Guid Id, IEnumerable Ranges) +{ +} + +public sealed record RangeRequest(long? TotalSize, long? Start, long? End) +{ + private const long DefaultBufferSize = 1024L * 512L; + + public long? Size + => Start.HasValue ? (End ?? Math.Min(TotalSize!.Value, Start.Value + DefaultBufferSize)) - Start.Value : End; + + public bool IsForFullFile + => Start is 0 && !End.HasValue; + + /// + /// Return Content-Range header content for this range + /// + /// + public string ToContentRange() + => $"bytes {Start}-{End ?? (Start + Size - 1L)}/{TotalSize?.ToString() ?? "*"}"; +} \ No newline at end of file diff --git a/VoidCat/Services/Abstractions/IStatsCollector.cs b/VoidCat/Services/Abstractions/IStatsCollector.cs new file mode 100644 index 0000000..d4d3233 --- /dev/null +++ b/VoidCat/Services/Abstractions/IStatsCollector.cs @@ -0,0 +1,12 @@ +namespace VoidCat.Services.Abstractions; + +public interface IStatsCollector +{ + ValueTask TrackIngress(Guid id, ulong amount); + ValueTask TrackEgress(Guid id, ulong amount); + + ValueTask GetBandwidth(); + ValueTask GetBandwidth(Guid id); +} + +public sealed record Bandwidth(ulong Ingress, ulong Egress); \ No newline at end of file diff --git a/VoidCat/Services/Abstractions/IUserManager.cs b/VoidCat/Services/Abstractions/IUserManager.cs new file mode 100644 index 0000000..7c41a62 --- /dev/null +++ b/VoidCat/Services/Abstractions/IUserManager.cs @@ -0,0 +1,10 @@ +namespace VoidCat.Services.Abstractions; + +public interface IUserManager +{ + ValueTask Get(string email, string password); + ValueTask Get(Guid id); + ValueTask Set(VoidUser user); +} + +public sealed record VoidUser(Guid Id, string Email, string PasswordHash); \ No newline at end of file diff --git a/VoidCat/Services/IFileMetadataStore.cs b/VoidCat/Services/IFileMetadataStore.cs deleted file mode 100644 index 10bef2a..0000000 --- a/VoidCat/Services/IFileMetadataStore.cs +++ /dev/null @@ -1,12 +0,0 @@ -using VoidCat.Model; - -namespace VoidCat.Services; - -public interface IFileMetadataStore -{ - Task Get(Guid id); - - Task Set(InternalVoidFile meta); - - Task Update(VoidFile patch, Guid editSecret); -} diff --git a/VoidCat/Services/IFileStore.cs b/VoidCat/Services/IFileStore.cs deleted file mode 100644 index 3433f3b..0000000 --- a/VoidCat/Services/IFileStore.cs +++ /dev/null @@ -1,48 +0,0 @@ -using VoidCat.Model; - -namespace VoidCat.Services -{ - public interface IFileStore - { - Task Get(Guid id); - - Task Ingress(IngressPayload payload, CancellationToken cts); - - Task Egress(EgressRequest request, Stream outStream, CancellationToken cts); - - Task UpdateInfo(VoidFile patch, Guid editSecret); - - IAsyncEnumerable ListFiles(); - } - - public record IngressPayload(Stream InStream, VoidFileMeta Meta, string Hash) - { - public Guid? Id { get; init; } - public Guid? EditSecret { get; init; } - - public bool IsAppend => Id.HasValue && EditSecret.HasValue; - } - - public record EgressRequest(Guid Id, IEnumerable Ranges) - { - } - - public record RangeRequest(long? TotalSize, long? Start, long? End) - { - private const long DefaultBufferSize = 1024L * 512L; - - public long? Size - => Start.HasValue ? - (End ?? Math.Min(TotalSize!.Value, Start.Value + DefaultBufferSize)) - Start.Value : End; - - public bool IsForFullFile - => Start is 0 && !End.HasValue; - - /// - /// Return Content-Range header content for this range - /// - /// - public string ToContentRange() - => $"bytes {Start}-{End ?? (Start + Size - 1L)}/{TotalSize?.ToString() ?? "*"}"; - } -} diff --git a/VoidCat/Services/IStatsCollector.cs b/VoidCat/Services/IStatsCollector.cs deleted file mode 100644 index b183aa6..0000000 --- a/VoidCat/Services/IStatsCollector.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace VoidCat.Services -{ - public interface IStatsCollector - { - ValueTask TrackIngress(Guid id, ulong amount); - ValueTask TrackEgress(Guid id, ulong amount); - - ValueTask GetBandwidth(); - ValueTask GetBandwidth(Guid id); - } - - public sealed record Bandwidth(ulong Ingress, ulong Egress); -} diff --git a/VoidCat/Services/InMemoryStatsCollector.cs b/VoidCat/Services/InMemoryStatsCollector.cs index 9cabc8a..69617d5 100644 --- a/VoidCat/Services/InMemoryStatsCollector.cs +++ b/VoidCat/Services/InMemoryStatsCollector.cs @@ -1,52 +1,52 @@ using Microsoft.Extensions.Caching.Memory; +using VoidCat.Services.Abstractions; -namespace VoidCat.Services +namespace VoidCat.Services; + +public class InMemoryStatsCollector : IStatsCollector { - public class InMemoryStatsCollector : IStatsCollector + private static Guid _global = new Guid("{A98DFDCC-C4E1-4D42-B818-912086FC6157}"); + private readonly IMemoryCache _cache; + + public InMemoryStatsCollector(IMemoryCache cache) { - private static Guid _global = new Guid("{A98DFDCC-C4E1-4D42-B818-912086FC6157}"); - private readonly IMemoryCache _cache; - - public InMemoryStatsCollector(IMemoryCache cache) - { - _cache = cache; - } - - public ValueTask TrackIngress(Guid id, ulong amount) - { - Incr(IngressKey(id), amount); - Incr(IngressKey(_global), amount); - return ValueTask.CompletedTask; - } - - public ValueTask TrackEgress(Guid id, ulong amount) - { - Incr(EgressKey(id), amount); - Incr(EgressKey(_global), amount); - return ValueTask.CompletedTask; - } - - public ValueTask GetBandwidth() - => ValueTask.FromResult(GetBandwidthInternal(_global)); - - public ValueTask GetBandwidth(Guid id) - => ValueTask.FromResult(GetBandwidthInternal(id)); - - private Bandwidth GetBandwidthInternal(Guid id) - { - var i = _cache.Get(IngressKey(id)) as ulong?; - var o = _cache.Get(EgressKey(id)) as ulong?; - return new(i ?? 0UL, o ?? 0UL); - } - - private void Incr(string k, ulong amount) - { - ulong v; - _cache.TryGetValue(k, out v); - _cache.Set(k, v + amount); - } - - private string IngressKey(Guid id) => $"stats:ingress:{id}"; - private string EgressKey(Guid id) => $"stats:egress:{id}"; + _cache = cache; } -} + + public ValueTask TrackIngress(Guid id, ulong amount) + { + Incr(IngressKey(id), amount); + Incr(IngressKey(_global), amount); + return ValueTask.CompletedTask; + } + + public ValueTask TrackEgress(Guid id, ulong amount) + { + Incr(EgressKey(id), amount); + Incr(EgressKey(_global), amount); + return ValueTask.CompletedTask; + } + + public ValueTask GetBandwidth() + => ValueTask.FromResult(GetBandwidthInternal(_global)); + + public ValueTask GetBandwidth(Guid id) + => ValueTask.FromResult(GetBandwidthInternal(id)); + + private Bandwidth GetBandwidthInternal(Guid id) + { + var i = _cache.Get(IngressKey(id)) as ulong?; + var o = _cache.Get(EgressKey(id)) as ulong?; + return new(i ?? 0UL, o ?? 0UL); + } + + private void Incr(string k, ulong amount) + { + ulong v; + _cache.TryGetValue(k, out v); + _cache.Set(k, v + amount); + } + + private string IngressKey(Guid id) => $"stats:ingress:{id}"; + private string EgressKey(Guid id) => $"stats:egress:{id}"; +} \ No newline at end of file diff --git a/VoidCat/Services/LocalDiskFileMetadataStore.cs b/VoidCat/Services/LocalDiskFileMetadataStore.cs index e432300..6dde4aa 100644 --- a/VoidCat/Services/LocalDiskFileMetadataStore.cs +++ b/VoidCat/Services/LocalDiskFileMetadataStore.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using VoidCat.Model; using VoidCat.Model.Exceptions; +using VoidCat.Services.Abstractions; namespace VoidCat.Services; @@ -20,23 +21,23 @@ public class LocalDiskFileMetadataStore : IFileMetadataStore } } - public async Task Get(Guid id) + public async ValueTask Get(Guid id) { var path = MapMeta(id); - if (!File.Exists(path)) throw new VoidFileNotFoundException(id); + if (!File.Exists(path)) return default; var json = await File.ReadAllTextAsync(path); return JsonConvert.DeserializeObject(json); } - public Task Set(InternalVoidFile meta) + public async ValueTask Set(InternalVoidFile meta) { var path = MapMeta(meta.Id); var json = JsonConvert.SerializeObject(meta); - return File.WriteAllTextAsync(path, json); + await File.WriteAllTextAsync(path, json); } - public async Task Update(VoidFile patch, Guid editSecret) + public async ValueTask Update(VoidFile patch, Guid editSecret) { var oldMeta = await Get(patch.Id); if (oldMeta?.EditSecret != editSecret) diff --git a/VoidCat/Services/LocalDiskFileStorage.cs b/VoidCat/Services/LocalDiskFileStorage.cs index 3594633..d312194 100644 --- a/VoidCat/Services/LocalDiskFileStorage.cs +++ b/VoidCat/Services/LocalDiskFileStorage.cs @@ -2,16 +2,17 @@ using System.Buffers; using System.Security.Cryptography; using VoidCat.Model; using VoidCat.Model.Exceptions; +using VoidCat.Services.Abstractions; namespace VoidCat.Services; -public class LocalDiskFileIngressFactory : IFileStore +public class LocalDiskFileStore : IFileStore { private readonly VoidSettings _settings; private readonly IStatsCollector _stats; private readonly IFileMetadataStore _metadataStore; - public LocalDiskFileIngressFactory(VoidSettings settings, IStatsCollector stats, + public LocalDiskFileStore(VoidSettings settings, IStatsCollector stats, IFileMetadataStore metadataStore) { _settings = settings; @@ -24,12 +25,12 @@ public class LocalDiskFileIngressFactory : IFileStore } } - public async Task Get(Guid id) + public async ValueTask Get(Guid id) { return await _metadataStore.Get(id); } - public async Task Egress(EgressRequest request, Stream outStream, CancellationToken cts) + public async ValueTask Egress(EgressRequest request, Stream outStream, CancellationToken cts) { var path = MapPath(request.Id); if (!File.Exists(path)) throw new VoidFileNotFoundException(request.Id); @@ -45,7 +46,7 @@ public class LocalDiskFileIngressFactory : IFileStore } } - public async Task Ingress(IngressPayload payload, CancellationToken cts) + public async ValueTask Ingress(IngressPayload payload, CancellationToken cts) { var id = payload.Id ?? Guid.NewGuid(); var fPath = MapPath(id); @@ -60,7 +61,7 @@ public class LocalDiskFileIngressFactory : IFileStore } // open file - await using var fsTemp = new FileStream(fPath, + await using var fsTemp = new FileStream(fPath, payload.IsAppend ? FileMode.Append : FileMode.Create, FileAccess.Write); var (total, hash) = await IngressInternal(id, payload.InStream, fsTemp, cts); @@ -69,6 +70,7 @@ public class LocalDiskFileIngressFactory : IFileStore { throw new CryptographicException("Invalid file hash"); } + if (payload.IsAppend) { vf = vf! with @@ -93,7 +95,7 @@ public class LocalDiskFileIngressFactory : IFileStore return vf; } - public Task UpdateInfo(VoidFile patch, Guid editSecret) + public ValueTask UpdateInfo(VoidFile patch, Guid editSecret) { return _metadataStore.Update(patch, editSecret); } @@ -123,9 +125,9 @@ public class LocalDiskFileIngressFactory : IFileStore { var buf = buffer.Memory[..readLength]; await fs.WriteAsync(buf, cts); - await _stats.TrackIngress(id, (ulong)readLength); + await _stats.TrackIngress(id, (ulong) readLength); sha.TransformBlock(buf.ToArray(), 0, buf.Length, null, 0); - total += (ulong)readLength; + total += (ulong) readLength; } sha.TransformFinalBlock(Array.Empty(), 0, 0); @@ -140,7 +142,7 @@ public class LocalDiskFileIngressFactory : IFileStore while ((readLength = await fileStream.ReadAsync(buffer.Memory, cts)) > 0) { await outStream.WriteAsync(buffer.Memory[..readLength], cts); - await _stats.TrackEgress(id, (ulong)readLength); + await _stats.TrackEgress(id, (ulong) readLength); await outStream.FlushAsync(cts); } } @@ -160,8 +162,8 @@ public class LocalDiskFileIngressFactory : IFileStore && dataRemaining > 0) { var toWrite = Math.Min(readLength, dataRemaining); - await outStream.WriteAsync(buffer.Memory[..(int)toWrite], cts); - await _stats.TrackEgress(id, (ulong)toWrite); + await outStream.WriteAsync(buffer.Memory[..(int) toWrite], cts); + await _stats.TrackEgress(id, (ulong) toWrite); dataRemaining -= toWrite; await outStream.FlushAsync(cts); } @@ -170,4 +172,4 @@ public class LocalDiskFileIngressFactory : IFileStore private string MapPath(Guid id) => Path.Join(_settings.DataDirectory, id.ToString()); -} +} \ No newline at end of file diff --git a/VoidCat/Services/PrometheusStatsCollector.cs b/VoidCat/Services/PrometheusStatsCollector.cs index e0c2a15..70e4544 100644 --- a/VoidCat/Services/PrometheusStatsCollector.cs +++ b/VoidCat/Services/PrometheusStatsCollector.cs @@ -1,14 +1,15 @@ using Prometheus; +using VoidCat.Services.Abstractions; namespace VoidCat.Services; public class PrometheusStatsCollector : IStatsCollector { private readonly Counter _egress = - Metrics.CreateCounter("egress", "Outgoing traffic from the site", new[] {"file"}); + Metrics.CreateCounter("egress", "Outgoing traffic from the site", "file"); private readonly Counter _ingress = - Metrics.CreateCounter("ingress", "Incoming traffic to the site", new[] {"file"}); + Metrics.CreateCounter("ingress", "Incoming traffic to the site", "file"); public ValueTask TrackIngress(Guid id, ulong amount) { diff --git a/VoidCat/VoidCat.csproj b/VoidCat/VoidCat.csproj index 19ae760..ab3d84f 100644 --- a/VoidCat/VoidCat.csproj +++ b/VoidCat/VoidCat.csproj @@ -11,6 +11,7 @@ + diff --git a/VoidCat/spa/src/FileUpload.js b/VoidCat/spa/src/FileUpload.js index ff9964d..c16331a 100644 --- a/VoidCat/spa/src/FileUpload.js +++ b/VoidCat/spa/src/FileUpload.js @@ -98,11 +98,11 @@ export function FileUpload(props) { } }; req.upload.onprogress = handleProgress; - req.open("POST", typeof(id) === "string" ? `/upload/${id}` : "/upload"); + req.open("POST", typeof (id) === "string" ? `/upload/${id}` : "/upload"); req.setRequestHeader("Content-Type", props.file.type); req.setRequestHeader("X-Filename", props.file.name); req.setRequestHeader("X-Digest", buf2hex(digest)); - if (typeof(editSecret) === "string") { + if (typeof (editSecret) === "string") { req.setRequestHeader("X-EditSecret", editSecret); } req.send(segment); @@ -113,19 +113,20 @@ export function FileUpload(props) { } async function doXHRUpload() { - const UploadSize = 100_000_000; - // upload file in segments of 100MB + // upload file in segments of 50MB + const UploadSize = 50_000_000; + let xhr = null; const segments = props.file.size / UploadSize; for (let s = 0; s < segments; s++) { let offset = s * UploadSize; let slice = props.file.slice(offset, offset + UploadSize, props.file.type); xhr = await xhrSegment(await slice.arrayBuffer(), xhr?.file?.id, xhr?.file?.editSecret); - if(!xhr.ok) { + if (!xhr.ok) { break; } } - if(xhr.ok) { + if (xhr.ok) { setUState(UploadState.Done); setResult(xhr.file); } else { diff --git a/VoidCat/spa/src/GlobalStats.css b/VoidCat/spa/src/GlobalStats.css index 4c89581..d105821 100644 --- a/VoidCat/spa/src/GlobalStats.css +++ b/VoidCat/spa/src/GlobalStats.css @@ -1,5 +1,5 @@ .stats { display: grid; grid-auto-flow: column; - margin: 0 100px; + margin: 0 30px; } \ No newline at end of file diff --git a/VoidCat/spa/src/GlobalStats.js b/VoidCat/spa/src/GlobalStats.js index a7b2c6e..2ae27e1 100644 --- a/VoidCat/spa/src/GlobalStats.js +++ b/VoidCat/spa/src/GlobalStats.js @@ -21,6 +21,8 @@ export function GlobalStats(props) {
{FormatBytes(stats?.bandwidth?.ingress ?? 0)}
Egress:
{FormatBytes(stats?.bandwidth?.egress ?? 0)}
+
Storage:
+
{FormatBytes(stats?.totalBytes ?? 0)}
); } \ No newline at end of file