diff --git a/Moonlight/App/Helpers/CacheLogger.cs b/Moonlight/App/Helpers/CacheLogger.cs index b8feb5ad..ef9e0c37 100644 --- a/Moonlight/App/Helpers/CacheLogger.cs +++ b/Moonlight/App/Helpers/CacheLogger.cs @@ -29,8 +29,11 @@ public class CacheLogger : ILogger } } - public void Info(string s) + public void Info(string? s) { + if (s == null) + return; + lock (Messages) { Messages.Add(new() @@ -39,12 +42,15 @@ public class CacheLogger : ILogger Message = s }); } - + SbLogger.Info(s); } - public void Debug(string s) + public void Debug(string? s) { + if (s == null) + return; + lock (Messages) { Messages.Add(new() @@ -53,12 +59,15 @@ public class CacheLogger : ILogger Message = s }); } - + SbLogger.Debug(s); } - public void Warn(string s) + public void Warn(string? s) { + if (s == null) + return; + lock (Messages) { Messages.Add(new() @@ -67,12 +76,15 @@ public class CacheLogger : ILogger Message = s }); } - + SbLogger.Warn(s); } - public void Error(string s) + public void Error(string? s) { + if (s == null) + return; + lock (Messages) { Messages.Add(new() @@ -81,12 +93,15 @@ public class CacheLogger : ILogger Message = s }); } - + SbLogger.Error(s); } - public void Fatal(string s) + public void Fatal(string? s) { + if (s == null) + return; + lock (Messages) { Messages.Add(new() @@ -95,7 +110,7 @@ public class CacheLogger : ILogger Message = s }); } - + SbLogger.Fatal(s); } @@ -109,7 +124,7 @@ public class CacheLogger : ILogger Message = ex.ToStringDemystified() }); } - + SbLogger.InfoEx(ex); } @@ -123,7 +138,7 @@ public class CacheLogger : ILogger Message = ex.ToStringDemystified() }); } - + SbLogger.DebugEx(ex); } @@ -137,7 +152,7 @@ public class CacheLogger : ILogger Message = ex.ToStringDemystified() }); } - + SbLogger.WarnEx(ex); } @@ -151,7 +166,7 @@ public class CacheLogger : ILogger Message = ex.ToStringDemystified() }); } - + SbLogger.ErrorEx(ex); } @@ -165,7 +180,7 @@ public class CacheLogger : ILogger Message = ex.ToStringDemystified() }); } - + SbLogger.FatalEx(ex); } diff --git a/Moonlight/App/Models/Forms/ServerOrderDataModel.cs b/Moonlight/App/Models/Forms/ServerOrderDataModel.cs new file mode 100644 index 00000000..778096f5 --- /dev/null +++ b/Moonlight/App/Models/Forms/ServerOrderDataModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class ServerOrderDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a server image")] + public Image Image { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/ServerRenameDataModel.cs b/Moonlight/App/Models/Forms/ServerRenameDataModel.cs new file mode 100644 index 00000000..619ee328 --- /dev/null +++ b/Moonlight/App/Models/Forms/ServerRenameDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class ServerRenameDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")] + public string Name { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Misc/SubscriptionLimit.cs b/Moonlight/App/Models/Misc/SubscriptionLimit.cs index 6e77abaf..21d59100 100644 --- a/Moonlight/App/Models/Misc/SubscriptionLimit.cs +++ b/Moonlight/App/Models/Misc/SubscriptionLimit.cs @@ -5,6 +5,12 @@ public class SubscriptionLimit public string Identifier { get; set; } = ""; public int Amount { get; set; } public List Options { get; set; } = new(); + + public string? ReadValue(string key) + { + var d = Options.FirstOrDefault(x => string.Equals(x.Key, key, StringComparison.InvariantCultureIgnoreCase)); + return d?.Value; + } public class LimitOption { diff --git a/Moonlight/App/Services/Interop/AlertService.cs b/Moonlight/App/Services/Interop/AlertService.cs index d03d4ef9..24a15fa4 100644 --- a/Moonlight/App/Services/Interop/AlertService.cs +++ b/Moonlight/App/Services/Interop/AlertService.cs @@ -5,10 +5,12 @@ namespace Moonlight.App.Services.Interop; public class AlertService { private readonly SweetAlertService SweetAlertService; + private readonly SmartTranslateService SmartTranslateService; - public AlertService(SweetAlertService service) + public AlertService(SweetAlertService service, SmartTranslateService smartTranslateService) { SweetAlertService = service; + SmartTranslateService = smartTranslateService; } public async Task Info(string title, string desciption) @@ -21,6 +23,11 @@ public class AlertService }); } + public async Task Info(string desciption) + { + await Info("", desciption); + } + public async Task Success(string title, string desciption) { await SweetAlertService.FireAsync(new SweetAlertOptions() @@ -31,6 +38,11 @@ public class AlertService }); } + public async Task Success(string desciption) + { + await Success("", desciption); + } + public async Task Warning(string title, string desciption) { await SweetAlertService.FireAsync(new SweetAlertOptions() @@ -41,6 +53,11 @@ public class AlertService }); } + public async Task Warning(string desciption) + { + await Warning("", desciption); + } + public async Task Error(string title, string desciption) { await SweetAlertService.FireAsync(new SweetAlertOptions() @@ -51,6 +68,11 @@ public class AlertService }); } + public async Task Error(string desciption) + { + await Error("", desciption); + } + public async Task YesNo(string title, string desciption, string yesText, string noText) { var result = await SweetAlertService.FireAsync(new SweetAlertOptions() @@ -79,4 +101,27 @@ public class AlertService return result.Value; } + + public async Task ConfirmMath() + { + var r = new Random(); + var i1 = r.Next(5, 15); + var i2 = r.Next(5, 15); + + var input = await Text( + SmartTranslateService.Translate("Confirm"), + $"{i1} + {i2} =", + "" + ); + + if (int.TryParse(input, out int i)) + { + if (i == i1 + i2) + { + return true; + } + } + + return false; + } } \ No newline at end of file diff --git a/Moonlight/App/Services/Interop/ClipboardService.cs b/Moonlight/App/Services/Interop/ClipboardService.cs index 79fa4202..a7c806c9 100644 --- a/Moonlight/App/Services/Interop/ClipboardService.cs +++ b/Moonlight/App/Services/Interop/ClipboardService.cs @@ -15,4 +15,9 @@ public class ClipboardService { await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data); } + public async Task Copy(string data) + { + await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data); + } + } \ No newline at end of file diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 7969bd78..8ec47c9f 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -1,11 +1,9 @@ -using Logging.Net; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Moonlight.App.Database; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; using Moonlight.App.Helpers; -using Moonlight.App.Models.Files; -using Moonlight.App.Models.Files.Accesses; +using Moonlight.App.Helpers.Files; using Moonlight.App.Models.Misc; using Moonlight.App.Models.Wings; using Moonlight.App.Models.Wings.Requests; @@ -13,6 +11,7 @@ using Moonlight.App.Models.Wings.Resources; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; using Moonlight.App.Services.LogServices; +using FileAccess = Moonlight.App.Helpers.Files.FileAccess; namespace Moonlight.App.Services; @@ -30,7 +29,6 @@ public class ServerService private readonly SecurityLogService SecurityLogService; private readonly AuditLogService AuditLogService; private readonly ErrorLogService ErrorLogService; - private readonly string AppUrl; public ServerService( ServerRepository serverRepository, @@ -58,8 +56,6 @@ public class ServerService SecurityLogService = securityLogService; AuditLogService = auditLogService; ErrorLogService = errorLogService; - - AppUrl = ConfigService.GetSection("Moonlight").GetValue("AppUrl"); } private Server EnsureNodeData(Server s) @@ -225,17 +221,17 @@ public class ServerService return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; } - public Task CreateFileAccess(Server s, User user) // We need the user to create the launch url + public Task CreateFileAccess(Server s, User user) // We need the user to create the launch url { Server server = EnsureNodeData(s); return Task.FromResult( - (IFileAccess)new WingsFileAccess( + (FileAccess)new WingsFileAccess( WingsApiHelper, - server, - user, WingsJwtHelper, - AppUrl + server, + ConfigService, + user ) ); } @@ -383,4 +379,20 @@ public class ServerService throw new Exception("User and owner id do not match"); } } + + public async Task Sync(Server s) + { + var server = EnsureNodeData(s); + + await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/sync", null); + } + + public async Task Delete(Server s) + { + var server = EnsureNodeData(s); + + await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null); + + ServerRepository.Delete(s); + } } \ No newline at end of file diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs new file mode 100644 index 00000000..0585c1dd --- /dev/null +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -0,0 +1,64 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services; + +public class SmartDeployService +{ + private readonly NodeRepository NodeRepository; + private readonly NodeService NodeService; + + public SmartDeployService(NodeRepository nodeRepository, NodeService nodeService) + { + NodeRepository = nodeRepository; + NodeService = nodeService; + } + + public async Task GetNode() + { + var data = new Dictionary(); + + foreach (var node in NodeRepository.Get().ToArray()) + { + var u = await GetUsageScore(node); + + if(u != 0) + data.Add(node, u); + } + + if (!data.Any()) + return null; + + return data.MaxBy(x => x.Value).Key; + } + + private async Task GetUsageScore(Node node) + { + var score = 0; + + try + { + var cpuStats = await NodeService.GetCpuStats(node); + var memoryStats = await NodeService.GetMemoryStats(node); + var diskStats = await NodeService.GetDiskStats(node); + + var cpuWeight = 0.5; // Weight of CPU usage in the final score + var memoryWeight = 0.3; // Weight of memory usage in the final score + var diskSpaceWeight = 0.2; // Weight of free disk space in the final score + + var cpuScore = (1 - cpuStats.Usage) * cpuWeight; // CPU score is based on the inverse of CPU usage + var memoryScore = (1 - (memoryStats.Used / 1024)) * memoryWeight; // Memory score is based on the percentage of free memory + var diskSpaceScore = (double) diskStats.FreeBytes / 1000000000 * diskSpaceWeight; // Disk space score is based on the amount of free disk space in GB + + var finalScore = cpuScore + memoryScore + diskSpaceScore; + + return finalScore; + } + catch (Exception e) + { + // ignored + } + + return score; + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/SubscriptionService.cs b/Moonlight/App/Services/SubscriptionService.cs index f5e77c95..de23692a 100644 --- a/Moonlight/App/Services/SubscriptionService.cs +++ b/Moonlight/App/Services/SubscriptionService.cs @@ -78,49 +78,45 @@ public class SubscriptionService await OneTimeJwtService.Revoke(code); } + public async Task Cancel() + { + if (await GetCurrent() != null) + { + var user = await GetCurrentUser(); + + user.CurrentSubscription = null; + + UserRepository.Update(user); + } + } + public async Task GetLimit(string identifier) { - var configSection = ConfigService.GetSection("Moonlight").GetSection("Subscriptions"); - - var defaultLimits = configSection.GetValue("defaultLimits"); - var subscription = await GetCurrent(); if (subscription == null) { - var foundDefault = defaultLimits.FirstOrDefault(x => x.Identifier == identifier); - - if (foundDefault != null) - return foundDefault; - return new() { Identifier = identifier, Amount = 0 }; } - else + + var subscriptionLimits = + JsonConvert.DeserializeObject(subscription.LimitsJson) + ?? Array.Empty(); + + var foundLimit = subscriptionLimits.FirstOrDefault(x => x.Identifier == identifier); + + if (foundLimit != null) + return foundLimit; + + return new() { - var subscriptionLimits = - JsonConvert.DeserializeObject(subscription.LimitsJson) - ?? Array.Empty(); - - var foundLimit = subscriptionLimits.FirstOrDefault(x => x.Identifier == identifier); - - if (foundLimit != null) - return foundLimit; - - var foundDefault = defaultLimits.FirstOrDefault(x => x.Identifier == identifier); - - if (foundDefault != null) - return foundDefault; - - return new() - { - Identifier = identifier, - Amount = 0 - }; - } + Identifier = identifier, + Amount = 0 + }; } private async Task GetCurrentUser() diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index d7629715..cb4fdcf3 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -91,6 +91,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Shared/Components/ServerControl/ServerSettings.razor b/Moonlight/Shared/Components/ServerControl/ServerSettings.razor index 280936c6..cd17ceed 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerSettings.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerSettings.razor @@ -1,5 +1,4 @@ -@using PteroConsole.NET -@using Moonlight.App.Database.Entities +@using Moonlight.App.Database.Entities @using Moonlight.Shared.Components.ServerControl.Settings @using Microsoft.AspNetCore.Components.Rendering @@ -53,7 +52,11 @@ if(Tags.Contains("pythonfile")) Settings.Add("Python file", typeof(PythonFileSetting)); - Settings.Add("Server reset", typeof(ServerResetSetting)); + Settings.Add("Rename", typeof(ServerRenameSetting)); + + Settings.Add("Reset", typeof(ServerResetSetting)); + + Settings.Add("Delete", typeof(ServerDeleteSetting)); return Task.CompletedTask; } diff --git a/Moonlight/Shared/Components/ServerControl/Settings/ServerDeleteSetting.razor b/Moonlight/Shared/Components/ServerControl/Settings/ServerDeleteSetting.razor new file mode 100644 index 00000000..aeb8bd69 --- /dev/null +++ b/Moonlight/Shared/Components/ServerControl/Settings/ServerDeleteSetting.razor @@ -0,0 +1,29 @@ +@using Moonlight.App.Database.Entities +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop + +@inject SmartTranslateService SmartTranslateService +@inject AlertService AlertService +@inject NavigationManager NavigationManager +@inject ServerService ServerService + + + + +@code +{ + [CascadingParameter] + public Server CurrentServer { get; set; } + + private async Task OnClick() + { + if (await AlertService.ConfirmMath()) + { + await ServerService.Delete(CurrentServer); + NavigationManager.NavigateTo("/servers", true); + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/ServerControl/Settings/ServerRenameSetting.razor b/Moonlight/Shared/Components/ServerControl/Settings/ServerRenameSetting.razor new file mode 100644 index 00000000..2c8185d4 --- /dev/null +++ b/Moonlight/Shared/Components/ServerControl/Settings/ServerRenameSetting.razor @@ -0,0 +1,41 @@ +@using Moonlight.App.Repositories.Servers +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms + +@inject ServerRepository ServerRepository + +
+ + +
+ + +
+
+
+
+ +@code +{ + [CascadingParameter] + public Server CurrentServer { get; set; } + + private ServerRenameDataModel Model = new(); + private LazyLoader LazyLoader; + + private Task Load(LazyLoader arg) + { + Model.Name = CurrentServer.Name; + + return Task.CompletedTask; + } + + private async Task Rename() + { + CurrentServer.Name = Model.Name; + + ServerRepository.Update(CurrentServer); + + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Layouts/MainLayout.razor b/Moonlight/Shared/Layouts/MainLayout.razor index 5345b49a..4c42ecb7 100644 --- a/Moonlight/Shared/Layouts/MainLayout.razor +++ b/Moonlight/Shared/Layouts/MainLayout.razor @@ -161,7 +161,8 @@ await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-page-loading"); await JsRuntime.InvokeVoidAsync("KTMenu.createInstances"); await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances"); - await JsRuntime.InvokeVoidAsync("createSnow"); + + //await JsRuntime.InvokeVoidAsync("createSnow"); await SessionService.Register(); diff --git a/Moonlight/Shared/Views/Admin/Servers/Edit.razor b/Moonlight/Shared/Views/Admin/Servers/Edit.razor index b3fc0b9a..95020ed6 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Edit.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Edit.razor @@ -8,6 +8,7 @@ @inject SmartTranslateService SmartTranslateService @inject ServerRepository ServerRepository +@inject ServerService ServerService @inject ImageRepository ImageRepository @@ -197,7 +198,8 @@ private async Task Save() { - ServerRepository.Update(Server); + ServerRepository.Update(Server!); + //await ServerService.Sync(Server!); I dont know if we need this, because wings should resync the data while restarting anyway await LazyLoader.Reload(); } diff --git a/Moonlight/Shared/Views/Admin/Subscriptions/Edit.razor b/Moonlight/Shared/Views/Admin/Subscriptions/Edit.razor new file mode 100644 index 00000000..87dd733c --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Subscriptions/Edit.razor @@ -0,0 +1,168 @@ +@page "/admin/subscriptions/edit/{Id:int}" +@using Moonlight.App.Models.Forms +@using Moonlight.App.Models.Misc +@using Moonlight.App.Repositories +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities + +@inject NavigationManager NavigationManager +@inject SubscriptionRepository SubscriptionRepository +@inject SubscriptionAdminService SubscriptionAdminService + + +
+ + @if (Subscription == null) + { +
+ No subscription with this id has been found +
+ } + else + { + + +
+ +
+ +
+ +
+ +
+ @foreach (var limitPart in Limits.Chunk(3)) + { +
+ @foreach (var limit in limitPart) + { +
+
+ +
+ +
+ +
+ +
+
+
+ Options +
+
+ +
+
+ + +
+
+
+
+ } +
+ } +
+ +
+ + +
+
+ } +
+
+
+ +@code +{ + [Parameter] + public int Id { get; set; } + + private Subscription? Subscription; + + private SubscriptionDataModel Model = new(); + private List Limits = new(); + + private async Task OnSubmit() + { + Subscription!.Name = Model.Name; + Subscription.Description = Model.Description; + + SubscriptionRepository.Update(Subscription); + + await SubscriptionAdminService.SaveLimits(Subscription, Limits.ToArray()); + + NavigationManager.NavigateTo("/admin/subscriptions"); + } + + private async Task Load(LazyLoader arg) + { + Subscription = SubscriptionRepository + .Get() + .FirstOrDefault(x => x.Id == Id); + + if (Subscription != null) + { + Model.Name = Subscription.Name; + Model.Description = Subscription.Description; + + Limits = (await SubscriptionAdminService.GetLimits(Subscription)).ToList(); + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Subscriptions/Index.razor b/Moonlight/Shared/Views/Admin/Subscriptions/Index.razor index 83c20654..e7182f6a 100644 --- a/Moonlight/Shared/Views/Admin/Subscriptions/Index.razor +++ b/Moonlight/Shared/Views/Admin/Subscriptions/Index.razor @@ -3,10 +3,15 @@ @using Moonlight.App.Database.Entities @using Moonlight.App.Repositories @using BlazorTable +@using Moonlight.App.Services.Interop @inject SmartTranslateService SmartTranslateService @inject SubscriptionRepository SubscriptionRepository +@inject SubscriptionAdminService SubscriptionAdminService +@inject AlertService AlertService +@inject ClipboardService ClipboardService +
@@ -36,9 +41,16 @@ - + @@ -66,7 +78,24 @@ private async Task Delete(Subscription subscription) { SubscriptionRepository.Delete(subscription); - + await LazyLoader.Reload(); } + + private async Task GenerateCode(Subscription subscription) + { + var durationText = await AlertService.Text( + SmartTranslateService.Translate("Duration"), + SmartTranslateService.Translate("Enter duration of subscription"), + "30" + ); + + if (int.TryParse(durationText, out int duration)) + { + var code = await SubscriptionAdminService.GenerateCode(subscription, duration); + + await ClipboardService.Copy(code); + await AlertService.Success(SmartTranslateService.Translate("Copied code to clipboard")); + } + } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Profile/Subscriptions.razor b/Moonlight/Shared/Views/Profile/Subscriptions.razor new file mode 100644 index 00000000..c8382775 --- /dev/null +++ b/Moonlight/Shared/Views/Profile/Subscriptions.razor @@ -0,0 +1,112 @@ +@page "/profile/subscriptions" + +@using Moonlight.Shared.Components.Navigations +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Services.Interop + +@inject ConfigService ConfigService +@inject AlertService AlertService +@inject SubscriptionService SubscriptionService +@inject SmartTranslateService SmartTranslateService + + + +
+
+
+ Subscription +
+
+
+ + @if (Subscription == null) + { + var config = ConfigService + .GetSection("Moonlight") + .GetSection("Subscriptions") + .GetSection("Sellpass"); + + var enableSellpass = config.GetValue("Enable"); + var url = config.GetValue("Url"); + +

+
+ + + +
+

+ + if (enableSellpass) + { + + } + } + else + { + var d = User.SubscriptionSince.AddDays(User.SubscriptionDuration).ToUniversalTime(); + +

+ Active until @(Formatter.FormatDateOnly(d)) +

+

+ Current subscription: @(Subscription.Name) +

+

+ @(Subscription.Description) +

+

+ We will send you a notification upon subscription expiration +

+
+ + +
+ } +
+
+
+
+
+ +@code +{ + [CascadingParameter] + public User User { get; set; } + + private Subscription? Subscription; + private LazyLoader LazyLoader; + + private string Code = ""; + + private async Task Load(LazyLoader arg) + { + Subscription = await SubscriptionService.GetCurrent(); + } + + private async Task Cancel() + { + if (await AlertService.ConfirmMath()) + { + await SubscriptionService.Cancel(); + await LazyLoader.Reload(); + } + } + + private async Task OnSubmit() + { + await SubscriptionService.ApplyCode(Code); + Code = ""; + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Server/Index.razor b/Moonlight/Shared/Views/Server/Index.razor index a6a6567d..eccbd3f9 100644 --- a/Moonlight/Shared/Views/Server/Index.razor +++ b/Moonlight/Shared/Views/Server/Index.razor @@ -17,6 +17,7 @@ @inject ServerRepository ServerRepository @inject WingsConsoleHelper WingsConsoleHelper @inject MessageService MessageService +@inject NodeService NodeService @inject NavigationManager NavigationManager @implements IDisposable @@ -26,10 +27,12 @@ {
- Not found image + Not found image
-

Server not found

-

+

+ Server not found +

+

A server with that id cannot be found or you have no access for this server

@@ -38,107 +41,126 @@ } else { - if (Console.ConnectionState == ConnectionState.Connected) + if (NodeOnline) { - if (Console.ServerState == ServerState.Installing) + if (Console.ConnectionState == ConnectionState.Connected) { -
-
-
-
- - Server installation is currently running - + if (Console.ServerState == ServerState.Installing) + { +
+
+
+
+ + Server installation is currently running + +
+
-
-
- } - else if (CurrentServer.Installing) - { -
-
-
-
- - Server installation is currently running - + } + else if (CurrentServer.Installing) + { +
+
+
+
+ + Server installation is currently running + +
+
-
-
- } - else - { - - - - - - - @{ - var index = 0; + } + else + { + + + + + + + @{ + var index = 0; - switch (Route) - { - case "files": - index = 1; - break; - case "backups": - index = 2; - break; - case "network": - index = 3; - break; - case "addons": - index = 4; - break; - case "settings": - index = 5; - break; - default: - index = 0; - break; + switch (Route) + { + case "files": + index = 1; + break; + case "backups": + index = 2; + break; + case "network": + index = 3; + break; + case "addons": + index = 4; + break; + case "settings": + index = 5; + break; + default: + index = 0; + break; + } } - } - - @switch (Route) - { - case "files": - - break; - case "backups": - - break; - case "network": - - break; - case "addons": - - break; - case "settings": - - break; - default: - - break; - } - + + @switch (Route) + { + case "files": + + break; + case "backups": + + break; + case "network": + + break; + case "addons": + + break; + case "settings": + + break; + default: + + break; + } + + - + } + } + else + { +
+ Connecting +
} } else { -
- Connecting +
+
+ Not found image +
+

+ Node offline +

+

+ The node the server is running on is currently offline +

+
+
} } @@ -149,7 +171,7 @@ [Parameter] public string ServerUuid { get; set; } - + [CascadingParameter] public User User { get; set; } @@ -159,6 +181,7 @@ private PteroConsole? Console; private Server? CurrentServer; private Node Node; + private bool NodeOnline = false; private Image Image; private NodeAllocation NodeAllocation; private string[] Tags; @@ -205,7 +228,7 @@ .Include(x => x.Owner) .First(x => x.Uuid == uuid); - if (CurrentServer.Owner.Id != User!.Id && User.Admin) + if (CurrentServer.Owner.Id != User!.Id && !User.Admin) CurrentServer = null; } catch (Exception) @@ -215,28 +238,43 @@ if (CurrentServer != null) { - await lazyLoader.SetText("Requesting tags"); + await lazyLoader.SetText("Checking node online status"); - var image = ImageRepository - .Get() - .First(x => x.Id == CurrentServer.Image.Id); - - Tags = JsonConvert.DeserializeObject(image.TagsJson) ?? Array.Empty(); - Image = image; - - await lazyLoader.SetText("Connecting to console"); - - await WingsConsoleHelper.ConnectWings(Console!, CurrentServer); - - MessageService.Subscribe($"server.{CurrentServer.Uuid}.installcomplete", this, server => + try { - Task.Run(() => + //TODO: Implement status caching + var data = await NodeService.GetStatus(CurrentServer.Node); + + if (data != null) + NodeOnline = true; + } + catch (Exception) + { + // ignored + } + + if (NodeOnline) + { + await lazyLoader.SetText("Requesting tags"); + + var image = ImageRepository + .Get() + .First(x => x.Id == CurrentServer.Image.Id); + + Tags = JsonConvert.DeserializeObject(image.TagsJson) ?? Array.Empty(); + Image = image; + + await lazyLoader.SetText("Connecting to console"); + + await WingsConsoleHelper.ConnectWings(Console!, CurrentServer); + + MessageService.Subscribe($"server.{CurrentServer.Uuid}.installcomplete", this, server => { - NavigationManager.NavigateTo(NavigationManager.Uri); + Task.Run(() => { NavigationManager.NavigateTo(NavigationManager.Uri); }); + + return Task.CompletedTask; }); - - return Task.CompletedTask; - }); + } } else { diff --git a/Moonlight/Shared/Views/Servers/Create.razor b/Moonlight/Shared/Views/Servers/Create.razor new file mode 100644 index 00000000..de782e1d --- /dev/null +++ b/Moonlight/Shared/Views/Servers/Create.razor @@ -0,0 +1,226 @@ +@page "/servers/create" +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms +@using Moonlight.App.Models.Misc +@using Moonlight.App.Repositories +@using Moonlight.App.Repositories.Servers +@using Microsoft.EntityFrameworkCore +@using Moonlight.App.Exceptions + +@inject SubscriptionService SubscriptionService +@inject ImageRepository ImageRepository +@inject SmartTranslateService SmartTranslateService +@inject SmartDeployService SmartDeployService +@inject ServerRepository ServerRepository +@inject NavigationManager NavigationManager +@inject ServerService ServerService + + + @if (DeployNode == null) + { +
+
+ Not found image +
+

+ No node found +

+

+ No node found to deploy to found +

+
+
+
+ } + else + { +
+
+
+
+
+

+ Server details +

+
+
+
+
+
+ +
@(DeployNode.Name)
+
+ @if (Model.Image != null) + { + var limit = Images[Model.Image]; + +
+ +
@(Model.Image.Name)
+
+ +
+ +
+ @{ + var cpu = limit.ReadValue("cpu"); + + if (cpu == null) + cpu = "N/A"; + else + cpu = (int.Parse(cpu) / 100).ToString(); + } + @(cpu) Cores +
+
+ +
+ +
@(limit.ReadValue("memory")) MB
+
+ +
+ +
@(limit.ReadValue("disk")) MB
+
+ } +
+
+
+
+
+
+
+
+

+ Configure your server +

+
+
+
+ + +
+ +
+ @if (Images.Any()) + { + + + + + + } + else + { +
+ + You reached the maximum amount of servers for every image of your subscription: @(Subscription == null ? SmartTranslateService.Translate("Default") : Subscription.Name) + +
+ } +
+
+
+
+
+ } +
+ +@code +{ + [CascadingParameter] + public User User { get; set; } + + private Node? DeployNode; + private Subscription? Subscription; + + private Dictionary Images = new(); + + private ServerOrderDataModel Model = new(); + + private async Task Load(LazyLoader lazyLoader) + { + // Reset state + Images.Clear(); + Model = new(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Loading your subscription")); + Subscription = await SubscriptionService.GetCurrent(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy node")); + + DeployNode = await SmartDeployService.GetNode(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Searching for available images")); + + var images = ImageRepository.Get().ToArray(); + + foreach (var image in images) + { + var limit = await SubscriptionService.GetLimit("image." + image.Id); + + if (limit.Amount > 0) + { + var serversCount = ServerRepository + .Get() + .Include(x => x.Owner) + .Include(x => x.Image) + .Where(x => x.Owner.Id == User.Id) + .Count(x => x.Image.Id == image.Id); + + if(serversCount <= limit.Amount) //TODO: FIX COUNTING + Images.Add(image, limit); + } + } + } + + private async Task OnValidSubmit() + { + var limit = await SubscriptionService.GetLimit("image." + Model.Image.Id); + + if (limit.Amount > 0) + { + var serversCount = ServerRepository + .Get() + .Include(x => x.Owner) + .Include(x => x.Image) + .Where(x => x.Owner.Id == User.Id) + .Count(x => x.Image.Id == Model.Image.Id); + + if (serversCount < limit.Amount) + { + if(int.TryParse(limit.ReadValue("cpu"), out int cpu) && + int.TryParse(limit.ReadValue("memory"), out int memory) && + int.TryParse(limit.ReadValue("disk"), out int disk)) + { + var server = await ServerService.Create( + Model.Name, + cpu, + memory, + disk, + User, + Model.Image, + DeployNode + ); + + NavigationManager.NavigateTo($"/server/{server.Uuid}"); + } + else + { + throw new DisplayException("Limits cannot be parsed"); + } + } + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Servers.razor b/Moonlight/Shared/Views/Servers/Index.razor similarity index 100% rename from Moonlight/Shared/Views/Servers.razor rename to Moonlight/Shared/Views/Servers/Index.razor diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 02ca6ef0..429e88a1 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -462,8 +462,23 @@ Create subscription;Create subscription Options;Options Amount;Amount Do you really want to delete it?;Do you really want to delete it? -Change your password;Change your password -You need to change your password in order to use moonlight;You need to change your password in order to use moonlight -You need to enter your full name in order to use moonlight;You need to enter your full name in order to use moonlight +Loading your subscription;Loading your subscription +Searching for deploy node;Searching for deploy node +Searching for available images;Searching for available images +Server details;Server details +Configure your server;Configure your server +Default;Default +You reached the maximum amount of servers for every image of your subscription;You reached the maximum amount of servers for every image of your subscription +Personal information;Personal information +Enter code;Enter code +Server rename;Server rename +Create code;Create code +Save subscription;Save subscription Enter your information;Enter your information -The field FirstName must be a string or array type with a minimum length of '2'.;The field FirstName must be a string or array type with a minimum length of '2'. +You need to enter your full name in order to use moonlight;You need to enter your full name in order to use moonlight +No node found;No node found +No node found to deploy to found;No node found to deploy to found +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Server not found;Server not found +A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server diff --git a/Moonlight/wwwroot/assets/media/svg/serverdown.svg b/Moonlight/wwwroot/assets/media/svg/serverdown.svg new file mode 100644 index 00000000..ca37dc52 --- /dev/null +++ b/Moonlight/wwwroot/assets/media/svg/serverdown.svg @@ -0,0 +1 @@ +server down \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/media/svg/subscription.svg b/Moonlight/wwwroot/assets/media/svg/subscription.svg new file mode 100644 index 00000000..6022690d --- /dev/null +++ b/Moonlight/wwwroot/assets/media/svg/subscription.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file