From 10c017932a263e795d2f8c108fdda1f22097a763 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 23 Aug 2023 17:29:34 +0200 Subject: [PATCH 1/2] Added a basic live statistics system --- Moonlight/App/Helpers/Formatter.cs | 26 +++ Moonlight/App/Perms/Permissions.cs | 9 +- Moonlight/Pages/_Layout.cshtml | 10 +- .../Index.razor} | 0 .../Shared/Views/Admin/Statistics/Live.razor | 201 ++++++++++++++++++ 5 files changed, 239 insertions(+), 7 deletions(-) rename Moonlight/Shared/Views/Admin/{Statistics.razor => Statistics/Index.razor} (100%) create mode 100644 Moonlight/Shared/Views/Admin/Statistics/Live.razor diff --git a/Moonlight/App/Helpers/Formatter.cs b/Moonlight/App/Helpers/Formatter.cs index 132a6fd9..05faba8c 100644 --- a/Moonlight/App/Helpers/Formatter.cs +++ b/Moonlight/App/Helpers/Formatter.cs @@ -156,6 +156,32 @@ public static class Formatter return (i / (1024D * 1024D)).Round(2) + " GB"; } } + + public static double CalculateAverage(List values) + { + if (values == null || values.Count == 0) + { + throw new ArgumentException("The list cannot be null or empty."); + } + + double sum = 0; + foreach (double value in values) + { + sum += value; + } + + return sum / values.Count; + } + + public static double CalculatePercentage(double part, double total) + { + if (total == 0) + { + return 0; + } + + return (part / total) * 100; + } public static RenderFragment FormatLineBreaks(string content) { diff --git a/Moonlight/App/Perms/Permissions.cs b/Moonlight/App/Perms/Permissions.cs index bc74ca82..cb6f4dab 100644 --- a/Moonlight/App/Perms/Permissions.cs +++ b/Moonlight/App/Perms/Permissions.cs @@ -410,10 +410,17 @@ public static class Permissions public static Permission AdminChangelog = new() { - Index = 59, + Index = 60, Name = "Admin changelog", Description = "View the changelog" }; + + public static Permission AdminStatisticsLive = new() + { + Index = 61, + Name = "Admin statistics live", + Description = "View the live statistics" + }; public static Permission? FromString(string name) { diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index c840ed2c..42e0443e 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -49,9 +49,7 @@ - - - + @@ -99,6 +97,9 @@ + + + @@ -122,9 +123,6 @@ moonlight.loading.registerXterm(); - - - diff --git a/Moonlight/Shared/Views/Admin/Statistics.razor b/Moonlight/Shared/Views/Admin/Statistics/Index.razor similarity index 100% rename from Moonlight/Shared/Views/Admin/Statistics.razor rename to Moonlight/Shared/Views/Admin/Statistics/Index.razor diff --git a/Moonlight/Shared/Views/Admin/Statistics/Live.razor b/Moonlight/Shared/Views/Admin/Statistics/Live.razor new file mode 100644 index 00000000..fc3e9a04 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Statistics/Live.razor @@ -0,0 +1,201 @@ +@page "/admin/statistics/live" + +@using Moonlight.App.Services +@using Moonlight.App.Repositories +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Services.Sessions + +@inject NodeService NodeService +@inject Repository NodeRepository +@inject IServiceScopeFactory ServiceScopeFactory + +@attribute [PermissionRequired(nameof(Permissions.AdminStatisticsLive))] + +
+
+
+
+
+ @(Math.Round(TotalCpuUsed, 2))% / 100% + + Total cpu load + +
+
+
+
+ @{ + var cpuPercent = Math.Round(Formatter.CalculatePercentage(TotalCpuUsed, 100)); + } + +
+ @(cpuPercent)% +
+ +
+
+
+
+
+
+
+ +
+
+
+
+ @(ByteSizeValue.FromKiloBytes(TotalMemoryUsed).GigaBytes)GB / @(ByteSizeValue.FromKiloBytes(TotalMemory).GigaBytes)GB + + Total memory load + +
+
+
+
+ @{ + var memoryPercent = Math.Round(Formatter.CalculatePercentage(TotalMemoryUsed, TotalMemory)); + } + +
+ @(memoryPercent)% +
+ +
+
+
+
+
+
+
+ +
+
+
+
+ @(Users) + + Total user count + +
+
+
+
+ +
+
+
+
+ @(Sessions) + + Total session count + +
+
+
+
+ +
+
+
+
+ @(ActiveUsers) + + Total active user count + +
+
+
+
+
+ +@code +{ + private long TotalMemoryUsed; + private long TotalMemory; + + private double TotalCpuUsed; + + private int Users; + private int ActiveUsers; + private int Sessions; + + protected override Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + Task.Run(async () => + { + while (true) + { + await Monitor(); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + }); + } + + return Task.CompletedTask; + } + + private async Task Monitor() + { + async Task Nodes() + { + TotalMemory = 0; + TotalMemoryUsed = 0; + + var cpuValues = new List(); + + foreach (var node in NodeRepository.Get().ToArray()) + { + try + { + var metrics = await NodeService.GetMemoryMetrics(node); + + TotalMemory += metrics.Total; + TotalMemoryUsed += metrics.Used; + + var cpuMetrics = await NodeService.GetCpuMetrics(node); + cpuValues.Add(cpuMetrics.CpuUsage); + } + catch (Exception) + { + // ignored + } + } + + TotalCpuUsed = Formatter.CalculateAverage(cpuValues); + + await InvokeAsync(StateHasChanged); + } + + async Task UsersAndSessions() + { + using var scope = ServiceScopeFactory.CreateScope(); + + var userRepo = scope.ServiceProvider.GetRequiredService>(); + var sessionService = scope.ServiceProvider.GetRequiredService(); + + Users = userRepo.Get().Count(); + Sessions = (await sessionService.GetSessions()).Length; + ActiveUsers = userRepo + .Get() + .Count(x => x.LastVisitedAt > DateTime.UtcNow.AddDays(-1)); + + await InvokeAsync(StateHasChanged); + } + + await Nodes(); + await UsersAndSessions(); + } + + private string GetStateColor(double percent) + { + if (percent < 60) + return "success"; + else if (percent >= 60 && percent < 80) + return "warning"; + else + return "danger"; + } +} \ No newline at end of file From 5793bd97479829c7bf5bb7fc4c36116295bc8c47 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 29 Aug 2023 16:07:43 +0200 Subject: [PATCH 2/2] Added new live statistics to navigation --- .../AdminStatisticsNavigation.razor | 22 +++++++++++++++++++ .../Shared/Views/Admin/Statistics/Index.razor | 5 ++++- .../Shared/Views/Admin/Statistics/Live.razor | 3 +++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Moonlight/Shared/Components/Navigations/AdminStatisticsNavigation.razor diff --git a/Moonlight/Shared/Components/Navigations/AdminStatisticsNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminStatisticsNavigation.razor new file mode 100644 index 00000000..9a913bc7 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminStatisticsNavigation.razor @@ -0,0 +1,22 @@ +
+ +
+ +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Statistics/Index.razor b/Moonlight/Shared/Views/Admin/Statistics/Index.razor index 8441a22a..5fc5aa3c 100644 --- a/Moonlight/Shared/Views/Admin/Statistics/Index.razor +++ b/Moonlight/Shared/Views/Admin/Statistics/Index.razor @@ -6,13 +6,16 @@ @using ApexCharts @using Moonlight.App.Helpers @using Moonlight.App.Services +@using Moonlight.Shared.Components.Navigations @inject StatisticsViewService StatisticsViewService @inject SmartTranslateService SmartTranslateService @attribute [PermissionRequired(nameof(Permissions.AdminStatistics))] -
+ + +