From 8c220e657674116c67c65cca34e54c71991163b0 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 15 Apr 2023 22:11:57 +0200 Subject: [PATCH 01/48] Added error handling in server backup (soft error handler). Added status code get accessor to api exceptions --- Moonlight/App/Exceptions/DaemonException.cs | 2 +- Moonlight/App/Exceptions/PleskException.cs | 2 +- Moonlight/App/Exceptions/WingsException.cs | 2 +- .../ErrorBoundaries/SoftErrorBoundary.razor | 4 ++ .../ServerControl/ServerBackups.razor | 68 +++++++++++-------- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/Moonlight/App/Exceptions/DaemonException.cs b/Moonlight/App/Exceptions/DaemonException.cs index 05070d4a..845c908f 100644 --- a/Moonlight/App/Exceptions/DaemonException.cs +++ b/Moonlight/App/Exceptions/DaemonException.cs @@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions; [Serializable] public class DaemonException : Exception { - public int StatusCode { private get; set; } + public int StatusCode { get; set; } public DaemonException() { diff --git a/Moonlight/App/Exceptions/PleskException.cs b/Moonlight/App/Exceptions/PleskException.cs index e29676f0..5ad8aa82 100644 --- a/Moonlight/App/Exceptions/PleskException.cs +++ b/Moonlight/App/Exceptions/PleskException.cs @@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions; [Serializable] public class PleskException : Exception { - public int StatusCode { private get; set; } + public int StatusCode { get; set; } public PleskException() { diff --git a/Moonlight/App/Exceptions/WingsException.cs b/Moonlight/App/Exceptions/WingsException.cs index a5dc9310..92b83b3f 100644 --- a/Moonlight/App/Exceptions/WingsException.cs +++ b/Moonlight/App/Exceptions/WingsException.cs @@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions; [Serializable] public class WingsException : Exception { - public int StatusCode { private get; set; } + public int StatusCode { get; set; } public WingsException() { diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index 3d59dd7c..fe27ce6a 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -57,6 +57,10 @@ else SmartTranslateService.Translate("Error from daemon"), wingsException.Message ); + + //TODO: Error log service + + Logger.Warn($"Wings exception status code: {wingsException.StatusCode}"); } else if (exception is PleskException pleskException) { diff --git a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor index 29f988dd..d0e76faf 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor @@ -186,86 +186,94 @@ private async Task Download(ServerBackup serverBackup) { + var url = await ServerService.DownloadBackup(CurrentServer, serverBackup); + + NavigationManager.NavigateTo(url); + await ToastService.Success(SmartTranslateService.Translate("Backup download successfully started")); + + /* try { - var url = await ServerService.DownloadBackup(CurrentServer, serverBackup); - - NavigationManager.NavigateTo(url); - await ToastService.Success(SmartTranslateService.Translate("Backup download successfully started")); + } catch (Exception e) { Logger.Warn("Error starting backup download"); Logger.Warn(e); await ToastService.Error(SmartTranslateService.Translate("Backup download failed")); - } + }*/ } private async Task CopyUrl(ServerBackup serverBackup) { + var url = await ServerService.DownloadBackup(CurrentServer, serverBackup); + + await ClipboardService.CopyToClipboard(url); + await AlertService.Success( + SmartTranslateService.Translate("Success"), + SmartTranslateService.Translate("Backup URL successfully copied to your clipboard")); + + /* try { - var url = await ServerService.DownloadBackup(CurrentServer, serverBackup); - - await ClipboardService.CopyToClipboard(url); - await AlertService.Success( - SmartTranslateService.Translate("Success"), - SmartTranslateService.Translate("Backup URL successfully copied to your clipboard")); + } catch (Exception e) { Logger.Warn("Error copying backup url"); Logger.Warn(e); await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while generating backup url")); - } + }*/ } private async Task Delete(ServerBackup serverBackup) { + await ToastService.Info(SmartTranslateService.Translate("Backup deletion started")); + await ServerService.DeleteBackup(CurrentServer, serverBackup); + + /* try { - await ToastService.Info(SmartTranslateService.Translate("Backup deletion started")); - await ServerService.DeleteBackup(CurrentServer, serverBackup); + } catch (Exception e) { Logger.Warn("Error deleting backup"); Logger.Warn(e); await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while starting backup deletion")); - } + }*/ } private async Task Restore(ServerBackup serverBackup) { + await ServerService.RestoreBackup(CurrentServer, serverBackup); + await ToastService.Info(SmartTranslateService.Translate("Backup restore started")); + + /* try { - await ServerService.RestoreBackup(CurrentServer, serverBackup); - - await ToastService.Info(SmartTranslateService.Translate("Backup restore started")); + } catch (Exception e) { Logger.Warn("Error restoring backup"); Logger.Warn(e); await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while restoring a backup")); - } + }*/ } private async Task Create() { + await ToastService.Info(SmartTranslateService.Translate("Started backup creation")); + await ServerService.CreateBackup(CurrentServer); + + /* try { - await ToastService.Info(SmartTranslateService.Translate("Started backup creation")); - var backup = await ServerService.CreateBackup(CurrentServer); - - /* - // Modify the backup list so no reload needed. Also the create event will work - var list = AllBackups!.ToList(); - list.Add(backup); - AllBackups = list.ToArray(); - - */ + //var list = AllBackups!.ToList(); + //list.Add(backup); + //AllBackups = list.ToArray(); await LazyLoader.Reload(); } @@ -274,7 +282,7 @@ Logger.Warn("Error creating backup"); Logger.Warn(e); await ToastService.Error(SmartTranslateService.Translate("An unknown error has occured while creating a backup")); - } + }*/ } private async Task OnContextMenuClick(ItemClickEventArgs args) From 9cdf293b7ea614258ae7d24fd721eacb1f21245f Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 15 Apr 2023 22:20:11 +0200 Subject: [PATCH 02/48] Added 404 handling for backup deletion --- Moonlight/App/Services/ServerService.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 2e46eb44..14f53944 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -186,9 +186,21 @@ public class ServerService .Include(x => x.Backups) .First(x => x.Id == server.Id); - await WingsApiHelper.Delete(serverData.Node, $"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}", - null); - + try + { + await WingsApiHelper.Delete(serverData.Node, $"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}", + null); + } + catch (WingsException e) + { + // when a backup is not longer there we can + // safely delete the backup so we ignore this error + if (e.StatusCode != 404) + { + throw; + } + } + var backup = serverData.Backups.First(x => x.Uuid == serverBackup.Uuid); serverData.Backups.Remove(backup); From 5e665b057e34c8ebc57053d2d9501f76d6678b17 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 15 Apr 2023 22:44:40 +0200 Subject: [PATCH 03/48] Implemented default subscription --- Moonlight/App/Services/SubscriptionService.cs | 29 ++++++++++++++----- .../configs/default_subscription.json | 1 + 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 Moonlight/defaultstorage/configs/default_subscription.json diff --git a/Moonlight/App/Services/SubscriptionService.cs b/Moonlight/App/Services/SubscriptionService.cs index de23692a..95c190da 100644 --- a/Moonlight/App/Services/SubscriptionService.cs +++ b/Moonlight/App/Services/SubscriptionService.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; +using Moonlight.App.Helpers; using Moonlight.App.Models.Misc; using Moonlight.App.Repositories; using Moonlight.App.Services.Sessions; @@ -14,20 +15,18 @@ public class SubscriptionService private readonly OneTimeJwtService OneTimeJwtService; private readonly IdentityService IdentityService; private readonly UserRepository UserRepository; - private readonly ConfigService ConfigService; public SubscriptionService( SubscriptionRepository subscriptionRepository, OneTimeJwtService oneTimeJwtService, IdentityService identityService, - UserRepository userRepository, - ConfigService configService) + UserRepository userRepository + ) { SubscriptionRepository = subscriptionRepository; OneTimeJwtService = oneTimeJwtService; IdentityService = identityService; UserRepository = userRepository; - ConfigService = configService; } public async Task GetCurrent() @@ -93,10 +92,12 @@ public class SubscriptionService public async Task GetLimit(string identifier) { var subscription = await GetCurrent(); + var defaultLimits = await GetDefaultLimits(); if (subscription == null) { - return new() + // If the default subscription limit with identifier is found, return it. if not, return empty + return defaultLimits.FirstOrDefault(x => x.Identifier == identifier) ?? new() { Identifier = identifier, Amount = 0 @@ -111,8 +112,9 @@ public class SubscriptionService if (foundLimit != null) return foundLimit; - - return new() + + // If the default subscription limit with identifier is found, return it. if not, return empty + return defaultLimits.FirstOrDefault(x => x.Identifier == identifier) ?? new() { Identifier = identifier, Amount = 0 @@ -133,4 +135,17 @@ public class SubscriptionService return userWithData; } + + private async Task GetDefaultLimits() // Add cache and reload option + { + var defaultSubscriptionJson = "[]"; + + if (File.Exists(PathBuilder.File("storage", "configs", "default_subscription.json"))) + { + defaultSubscriptionJson = + await File.ReadAllTextAsync(PathBuilder.File("storage", "configs", "default_subscription.json")); + } + + return JsonConvert.DeserializeObject(defaultSubscriptionJson) ?? Array.Empty(); + } } \ No newline at end of file diff --git a/Moonlight/defaultstorage/configs/default_subscription.json b/Moonlight/defaultstorage/configs/default_subscription.json new file mode 100644 index 00000000..ad47dbb9 --- /dev/null +++ b/Moonlight/defaultstorage/configs/default_subscription.json @@ -0,0 +1 @@ +[] \ No newline at end of file From 30106c2c4589e3ef67e0aafd27b3c860163f5826 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 15 Apr 2023 22:57:29 +0200 Subject: [PATCH 04/48] Implemented random loading message --- Moonlight/App/Extensions/DbSetExtensions.cs | 14 ++++++++++++++ Moonlight/Pages/_Layout.cshtml | 9 ++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Moonlight/App/Extensions/DbSetExtensions.cs diff --git a/Moonlight/App/Extensions/DbSetExtensions.cs b/Moonlight/App/Extensions/DbSetExtensions.cs new file mode 100644 index 00000000..7f06c81c --- /dev/null +++ b/Moonlight/App/Extensions/DbSetExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace Moonlight.App.Extensions; + +public static class DbSetExtensions +{ + public static T Random(this DbSet repo) where T : class + { + Random rand = new Random(); + int toSkip = rand.Next(0, repo.Count()); + + return repo.Skip(toSkip).Take(1).First(); + } +} \ No newline at end of file diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index 7a5518e6..ef045fad 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -1,9 +1,12 @@ @using Microsoft.AspNetCore.Components.Web +@using Moonlight.App.Extensions +@using Moonlight.App.Repositories @using Moonlight.App.Services @namespace Moonlight.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @inject ConfigService ConfigService +@inject LoadingMessageRepository LoadingMessageRepository @{ var headerConfig = ConfigService @@ -73,9 +76,13 @@
Logo + @{ + var loadingMessage = LoadingMessageRepository.Get().Random(); + } +
- CHANGEME + @(loadingMessage.Message)
From 2e83b1d82898f2aee55ce13cd1ac53d497f93670 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 00:16:58 +0200 Subject: [PATCH 05/48] Optimized interop client side --- .../App/Services/Interop/ClipboardService.cs | 9 +- .../App/Services/Interop/ToastService.cs | 14 +- Moonlight/Pages/_Layout.cshtml | 8 + .../FileManagerPartials/FileEditor.razor | 2 +- .../Components/Partials/ThemeSwitcher.razor | 2 +- .../ServerControl/ServerBackups.razor | 2 +- .../Shared/Components/Xterm/Terminal.razor | 4 +- Moonlight/Shared/Layouts/MainLayout.razor | 2 +- Moonlight/wwwroot/assets/js/alertUtils.js | 68 ---- Moonlight/wwwroot/assets/js/clipboard.js | 32 -- Moonlight/wwwroot/assets/js/consoleUtils.js | 143 -------- Moonlight/wwwroot/assets/js/cookieUtils.js | 26 -- Moonlight/wwwroot/assets/js/flashbang.js | 12 - Moonlight/wwwroot/assets/js/loggingUtils.js | 51 --- Moonlight/wwwroot/assets/js/monacoTheme.js | 12 - Moonlight/wwwroot/assets/js/moonlight.js | 347 +++++++++++++++++- Moonlight/wwwroot/assets/js/recaptcha.js | 9 - Moonlight/wwwroot/assets/js/snow.js | 118 ------ Moonlight/wwwroot/assets/js/toastUtils.js | 52 --- Moonlight/wwwroot/assets/js/utils.js | 42 --- Moonlight/wwwroot/assets/js/xtermAddons.js | 3 - 21 files changed, 360 insertions(+), 598 deletions(-) delete mode 100644 Moonlight/wwwroot/assets/js/alertUtils.js delete mode 100644 Moonlight/wwwroot/assets/js/clipboard.js delete mode 100644 Moonlight/wwwroot/assets/js/consoleUtils.js delete mode 100644 Moonlight/wwwroot/assets/js/cookieUtils.js delete mode 100644 Moonlight/wwwroot/assets/js/flashbang.js delete mode 100644 Moonlight/wwwroot/assets/js/loggingUtils.js delete mode 100644 Moonlight/wwwroot/assets/js/monacoTheme.js delete mode 100644 Moonlight/wwwroot/assets/js/recaptcha.js delete mode 100644 Moonlight/wwwroot/assets/js/snow.js delete mode 100644 Moonlight/wwwroot/assets/js/toastUtils.js delete mode 100644 Moonlight/wwwroot/assets/js/utils.js delete mode 100644 Moonlight/wwwroot/assets/js/xtermAddons.js diff --git a/Moonlight/App/Services/Interop/ClipboardService.cs b/Moonlight/App/Services/Interop/ClipboardService.cs index a7c806c9..6a702282 100644 --- a/Moonlight/App/Services/Interop/ClipboardService.cs +++ b/Moonlight/App/Services/Interop/ClipboardService.cs @@ -10,14 +10,9 @@ public class ClipboardService { JsRuntime = jsRuntime; } - - public async Task CopyToClipboard(string data) - { - await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data); - } + public async Task Copy(string data) { - await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data); + await JsRuntime.InvokeVoidAsync("moonlight.clipboard.copy", data); } - } \ No newline at end of file diff --git a/Moonlight/App/Services/Interop/ToastService.cs b/Moonlight/App/Services/Interop/ToastService.cs index 5e1839d3..af66d12a 100644 --- a/Moonlight/App/Services/Interop/ToastService.cs +++ b/Moonlight/App/Services/Interop/ToastService.cs @@ -13,36 +13,36 @@ public class ToastService public async Task Info(string message) { - await JsRuntime.InvokeVoidAsync("showInfoToast", message); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.info", message); } public async Task Error(string message) { - await JsRuntime.InvokeVoidAsync("showErrorToast", message); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.error", message); } public async Task Warning(string message) { - await JsRuntime.InvokeVoidAsync("showWarningToast", message); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.warning", message); } public async Task Success(string message) { - await JsRuntime.InvokeVoidAsync("showSuccessToast", message); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.success", message); } public async Task CreateProcessToast(string id, string text) { - await JsRuntime.InvokeVoidAsync("createToast", id, text); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.create", id, text); } public async Task UpdateProcessToast(string id, string text) { - await JsRuntime.InvokeVoidAsync("modifyToast", id, text); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.modify", id, text); } public async Task RemoveProcessToast(string id) { - await JsRuntime.InvokeVoidAsync("removeToast", id); + await JsRuntime.InvokeVoidAsync("moonlight.toasts.remove", id); } } \ No newline at end of file diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index ef045fad..74bbf010 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -100,15 +100,21 @@ +@* +*@ + +@* +*@ +@* @@ -118,7 +124,9 @@ +*@ + diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor index 9af01b0f..0c584993 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor @@ -71,7 +71,7 @@ { if (firstRender) { - await JsRuntime.InvokeVoidAsync("initMonacoTheme"); + await JsRuntime.InvokeVoidAsync("moonlight.loading.loadMonaco"); Editor.OnDidInit = new EventCallback(this, async () => { diff --git a/Moonlight/Shared/Components/Partials/ThemeSwitcher.razor b/Moonlight/Shared/Components/Partials/ThemeSwitcher.razor index 8b858667..3895beb9 100644 --- a/Moonlight/Shared/Components/Partials/ThemeSwitcher.razor +++ b/Moonlight/Shared/Components/Partials/ThemeSwitcher.razor @@ -47,6 +47,6 @@ private async void TriggerFlashbang() { - await JsRuntime.InvokeVoidAsync("flashbang"); + await JsRuntime.InvokeVoidAsync("moonlight.flashbang.run"); } } \ No newline at end of file diff --git a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor index d0e76faf..5c8453f8 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor @@ -208,7 +208,7 @@ { var url = await ServerService.DownloadBackup(CurrentServer, serverBackup); - await ClipboardService.CopyToClipboard(url); + await ClipboardService.Copy(url); await AlertService.Success( SmartTranslateService.Translate("Success"), SmartTranslateService.Translate("Backup URL successfully copied to your clipboard")); diff --git a/Moonlight/Shared/Components/Xterm/Terminal.razor b/Moonlight/Shared/Components/Xterm/Terminal.razor index 6a1c6932..6d8e2a6f 100644 --- a/Moonlight/Shared/Components/Xterm/Terminal.razor +++ b/Moonlight/Shared/Components/Xterm/Terminal.razor @@ -16,7 +16,7 @@ [Parameter] public Action RunOnFirstRender { get; set; } - private TerminalOptions TerminalOptions = new TerminalOptions + private TerminalOptions TerminalOptions = new() { CursorBlink = false, CursorStyle = CursorStyle.Underline, @@ -33,6 +33,7 @@ } catch (Exception) { + // ignored } } @@ -50,6 +51,7 @@ } catch (Exception) { + // ignored } } } \ No newline at end of file diff --git a/Moonlight/Shared/Layouts/MainLayout.razor b/Moonlight/Shared/Layouts/MainLayout.razor index 46017b54..064fd5c7 100644 --- a/Moonlight/Shared/Layouts/MainLayout.razor +++ b/Moonlight/Shared/Layouts/MainLayout.razor @@ -160,7 +160,7 @@ await JsRuntime.InvokeVoidAsync("KTMenu.createInstances"); await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances"); - //await JsRuntime.InvokeVoidAsync("createSnow"); + await JsRuntime.InvokeVoidAsync("moonlight.loading.registerXterm"); await SessionService.Register(); diff --git a/Moonlight/wwwroot/assets/js/alertUtils.js b/Moonlight/wwwroot/assets/js/alertUtils.js deleted file mode 100644 index 5e46f3be..00000000 --- a/Moonlight/wwwroot/assets/js/alertUtils.js +++ /dev/null @@ -1,68 +0,0 @@ -window.showAlertInfo = function (title, description) -{ - Swal.fire( - title, - description, - 'info' - ) -} - -window.showAlertSuccess = function (title, description) -{ - Swal.fire( - title, - description, - 'success' - ) -} - -window.showAlertWarning = function (title, description) -{ - Swal.fire( - title, - description, - 'warning' - ) -} - -window.showAlertError = function (title, description) -{ - Swal.fire( - title, - description, - 'error' - ) -} - -window.showAlertYesNo = function (title, yesText, noText) -{ - return Swal.fire({ - title: title, - showDenyButton: true, - confirmButtonText: yesText, - denyButtonText: noText, - }).then((result) => { - if (result.isConfirmed) { - return true; - } else if (result.isDenied) { - return false; - } - }) -} - -window.showAlertText = function (title, description) { - const {value: text} = Swal.fire({ - title: title, - input: 'text', - inputLabel: description, - inputValue: "", - showCancelButton: false, - inputValidator: (value) => { - if (!value) { - return 'Es muss ein Wert angegeben werden' - } - } - }) - - return text; -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/clipboard.js b/Moonlight/wwwroot/assets/js/clipboard.js deleted file mode 100644 index 22b3a49d..00000000 --- a/Moonlight/wwwroot/assets/js/clipboard.js +++ /dev/null @@ -1,32 +0,0 @@ -function fallbackCopyTextToClipboard(text) { - var textArea = document.createElement("textarea"); - textArea.value = text; - - // Avoid scrolling to bottom - textArea.style.top = "0"; - textArea.style.left = "0"; - textArea.style.position = "fixed"; - - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - - try { - var successful = document.execCommand('copy'); - var msg = successful ? 'successful' : 'unsuccessful'; - } catch (err) { - console.error('Fallback: Oops, unable to copy', err); - } - - document.body.removeChild(textArea); -} -function copyTextToClipboard(text) { - if (!navigator.clipboard) { - fallbackCopyTextToClipboard(text); - return; - } - navigator.clipboard.writeText(text).then(function() { - }, function(err) { - console.error('Async: Could not copy text: ', err); - }); -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/consoleUtils.js b/Moonlight/wwwroot/assets/js/consoleUtils.js deleted file mode 100644 index 14c1cf31..00000000 --- a/Moonlight/wwwroot/assets/js/consoleUtils.js +++ /dev/null @@ -1,143 +0,0 @@ -window.initConsolePlugins = function () { - window.XtermBlazor.registerAddon("xterm-addon-fit", new window.FitAddon.FitAddon()); - - addEventListener('resize', (event) => { - //XtermBlazor.invokeAddonFunction() - }); -}; - -window.initGraphs = function () { - -}; - -window.initCpu = function () { - var element = document.getElementById('cpuchart'); - - var height = parseInt(KTUtil.css(element, 'height')); - var labelColor = KTUtil.getCssVariableValue('--kt-gray-500'); - var borderColor = KTUtil.getCssVariableValue('--kt-gray-200'); - var baseColor = KTUtil.getCssVariableValue('--kt-info'); - var lightColor = KTUtil.getCssVariableValue('--kt-info-light'); - - if (!element) { - return; - } - - var options = { - series: [{ - name: 'CPU Auslastung', - data: [0, 0, 0, 0, 0, 0, 0] - }], - chart: { - fontFamily: 'inherit', - type: 'area', - height: height, - toolbar: { - show: false - } - }, - plotOptions: {}, - legend: { - show: false - }, - dataLabels: { - enabled: false - }, - fill: { - type: 'solid', - opacity: 1 - }, - stroke: { - curve: 'smooth', - show: true, - width: 3, - colors: [baseColor] - }, - xaxis: { - categories: [], - axisBorder: { - show: false, - }, - axisTicks: { - show: false - }, - labels: { - style: { - colors: labelColor, - fontSize: '12px' - } - }, - crosshairs: { - position: 'front', - stroke: { - color: baseColor, - width: 1, - dashArray: 3 - } - }, - tooltip: { - enabled: false, - formatter: undefined, - offsetY: 0, - style: { - fontSize: '12px' - } - } - }, - yaxis: { - labels: { - style: { - colors: labelColor, - fontSize: '12px' - } - } - }, - states: { - normal: { - filter: { - type: 'none', - value: 0 - } - }, - hover: { - filter: { - type: 'none', - value: 0 - } - }, - active: { - allowMultipleDataPointsSelection: false, - filter: { - type: 'none', - value: 0 - } - } - }, - colors: [lightColor], - grid: { - borderColor: borderColor, - strokeDashArray: 4, - yaxis: { - lines: { - show: true - } - } - }, - markers: { - strokeColor: baseColor, - strokeWidth: 3 - } - }; - - var chart = new ApexCharts(element, options); - chart.render(); - - window.console.cpuchart = chart; -}; - -window.updateCpu = function (value) { - window.console.cpuchart.appendSeries({ - name: 'CPU Auslastung', - data: value - }); -}; \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/cookieUtils.js b/Moonlight/wwwroot/assets/js/cookieUtils.js deleted file mode 100644 index f64894f1..00000000 --- a/Moonlight/wwwroot/assets/js/cookieUtils.js +++ /dev/null @@ -1,26 +0,0 @@ -window.setCookie = function (key, value, days) { - const d = new Date(); - d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); - let expires = "expires=" + d.toUTCString(); - document.cookie = key + "=" + value + ";" + expires + ";path=/"; -} - -window.deleteCookie = function (key) { - window.setCookie(key, "", 0); -} - -window.getCookie = function (cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return ""; -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/flashbang.js b/Moonlight/wwwroot/assets/js/flashbang.js deleted file mode 100644 index 44f1996b..00000000 --- a/Moonlight/wwwroot/assets/js/flashbang.js +++ /dev/null @@ -1,12 +0,0 @@ -function flashbang() { - const light = document.getElementById("flashbang"); - light.style.boxShadow = "0 0 10000px 10000px white, 0 0 250px 10px #FFFFFF"; - light.style.animation = "flashbang 5s linear forwards"; - light.onanimationend = clearFlashbang; -} - -function clearFlashbang() { - const light = document.getElementById("flashbang"); - light.style.animation = ""; - light.style.opacity = "0"; -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/loggingUtils.js b/Moonlight/wwwroot/assets/js/loggingUtils.js deleted file mode 100644 index 9a82f504..00000000 --- a/Moonlight/wwwroot/assets/js/loggingUtils.js +++ /dev/null @@ -1,51 +0,0 @@ -window.logInfo = function (prefix, message) -{ - console.log( - '%c[%cINFO%c] [%c' + prefix + '%c] %c' + message, - 'color: white', // [ - 'color: aqua', // INFO - 'color: white', // ] - 'color: purple', // {prefix} - 'color: white', // ] - 'color: lightgray' // {message} - ); -}; - -window.logWarn = function (prefix, message) -{ - console.log( - '%c[%cWARN%c] [%c' + prefix + '%c] %c' + message, - 'color: white', // [ - 'color: orange', // WARN - 'color: white', // ] - 'color: purple', // {prefix} - 'color: white', // ] - 'color: lightgray' // {message} - ); -}; - -window.logError = function (prefix, message) -{ - console.log( - '%c[%cERROR%c] [%c' + prefix + '%c] %c' + message, - 'color: white', // [ - 'color: red', // ERROR - 'color: white', // ] - 'color: purple', // {prefix} - 'color: white', // ] - 'color: lightgray' // {message} - ); -}; - -window.logDebug = function (prefix, message) -{ - console.log( - '%c[%cDEBUG%c] [%c' + prefix + '%c] %c' + message, - 'color: white', // [ - 'color: green', // DEBUG - 'color: white', // ] - 'color: purple', // {prefix} - 'color: white', // ] - 'color: lightgray' // {message} - ); -}; \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/monacoTheme.js b/Moonlight/wwwroot/assets/js/monacoTheme.js deleted file mode 100644 index 168437a4..00000000 --- a/Moonlight/wwwroot/assets/js/monacoTheme.js +++ /dev/null @@ -1,12 +0,0 @@ -window.initMonacoTheme = function () -{ - monaco.editor.defineTheme('moonlight-theme', { - base: 'vs-dark', - inherit: true, - rules: [ - ], - colors: { - 'editor.background': '#000000' - } - }); -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/moonlight.js b/Moonlight/wwwroot/assets/js/moonlight.js index 55e11d6f..a1675549 100644 --- a/Moonlight/wwwroot/assets/js/moonlight.js +++ b/Moonlight/wwwroot/assets/js/moonlight.js @@ -1,13 +1,338 @@ -window.moonlight = -{ - modals: { - show: function (name) - { - $('#' + name).modal('show'); +window.moonlight = + { + modals: { + show: function (name) { + $('#' + name).modal('show'); + }, + hide: function (name) { + $('#' + name).modal('hide'); + } }, - hide: function (name) - { - $('#' + name).modal('hide'); + alerts: { + info: function (title, description) { + Swal.fire( + title, + description, + 'info' + ) + }, + success: function (title, description) { + Swal.fire( + title, + description, + 'success' + ) + }, + warning: function (title, description) { + Swal.fire( + title, + description, + 'warning' + ) + }, + error: function (title, description) { + Swal.fire( + title, + description, + 'error' + ) + }, + yesno: function (title, yesText, noText) { + return Swal.fire({ + title: title, + showDenyButton: true, + confirmButtonText: yesText, + denyButtonText: noText, + }).then((result) => { + if (result.isConfirmed) { + return true; + } else if (result.isDenied) { + return false; + } + }) + }, + text: function (title, description) { + const {value: text} = Swal.fire({ + title: title, + input: 'text', + inputLabel: description, + inputValue: "", + showCancelButton: false, + inputValidator: (value) => { + if (!value) { + return 'You need to enter a value' + } + } + }) + + return text; + } + }, + clipboard: { + copy: function (text) { + if (!navigator.clipboard) { + var textArea = document.createElement("textarea"); + textArea.value = text; + + // Avoid scrolling to bottom + textArea.style.top = "0"; + textArea.style.left = "0"; + textArea.style.position = "fixed"; + + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + var successful = document.execCommand('copy'); + var msg = successful ? 'successful' : 'unsuccessful'; + } catch (err) { + console.error('Fallback: Oops, unable to copy', err); + } + + document.body.removeChild(textArea); + return; + } + navigator.clipboard.writeText(text).then(function () { + }, + function (err) { + console.error('Async: Could not copy text: ', err); + } + ); + } + }, + recaptcha: { + render: function (id, sitekey, page) { + return grecaptcha.render(id, { + 'sitekey': sitekey, + 'callback': (response) => { + page.invokeMethodAsync('CallbackOnSuccess', response); + }, + 'expired-callback': () => { + page.invokeMethodAsync('CallbackOnExpired'); + } + }); + } + }, + snow: { + create: function () { + (function () { + var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + window.requestAnimationFrame = requestAnimationFrame; + })(); + + + var flakes = [], + canvas = document.getElementById("snow"), + ctx = canvas.getContext("2d"), + flakeCount = 200, + mX = -100, + mY = -100 + + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + function snow() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + for (var i = 0; i < flakeCount; i++) { + var flake = flakes[i], + x = mX, + y = mY, + minDist = 150, + x2 = flake.x, + y2 = flake.y; + + var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)), + dx = x2 - x, + dy = y2 - y; + + if (dist < minDist) { + var force = minDist / (dist * dist), + xcomp = (x - x2) / dist, + ycomp = (y - y2) / dist, + deltaV = force / 2; + + flake.velX -= deltaV * xcomp; + flake.velY -= deltaV * ycomp; + + } else { + flake.velX *= .98; + if (flake.velY <= flake.speed) { + flake.velY = flake.speed + } + flake.velX += Math.cos(flake.step += .05) * flake.stepSize; + } + + ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")"; + flake.y += flake.velY; + flake.x += flake.velX; + + if (flake.y >= canvas.height || flake.y <= 0) { + reset(flake); + } + + + if (flake.x >= canvas.width || flake.x <= 0) { + reset(flake); + } + + ctx.beginPath(); + ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2); + ctx.fill(); + } + requestAnimationFrame(snow); + }; + + function reset(flake) { + flake.x = Math.floor(Math.random() * canvas.width); + flake.y = 0; + flake.size = (Math.random() * 3) + 2; + flake.speed = (Math.random() * 1) + 0.5; + flake.velY = flake.speed; + flake.velX = 0; + flake.opacity = (Math.random() * 0.5) + 0.3; + } + + function init() { + for (var i = 0; i < flakeCount; i++) { + var x = Math.floor(Math.random() * canvas.width), + y = Math.floor(Math.random() * canvas.height), + size = (Math.random() * 3) + 2, + speed = (Math.random() * 1) + 0.5, + opacity = (Math.random() * 0.5) + 0.3; + + flakes.push({ + speed: speed, + velY: speed, + velX: 0, + x: x, + y: y, + size: size, + stepSize: (Math.random()) / 30, + step: 0, + opacity: opacity + }); + } + + snow(); + }; + + canvas.addEventListener("mousemove", function (e) { + mX = e.clientX, + mY = e.clientY + }); + + window.addEventListener("resize", function () { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }) + + init(); + } + }, + toasts: { + info: function (msg) { + toastr['info'](msg); + }, + error: function (msg) { + toastr['error'](msg); + }, + success: function (msg) { + toastr['success'](msg); + }, + warning: function (msg) { + toastr['warning'](msg); + }, + create: function (id, text) { + var toast = toastr.success(text, '', + { + closeButton: true, + progressBar: false, + tapToDismiss: false, + timeOut: 0, + extendedTimeOut: 0, + positionClass: "toastr-bottom-right", + preventDuplicates: false, + onclick: function () { + toastr.clear(toast); + } + }); + var toastElement = toast[0]; + toastElement.setAttribute('data-toast-id', id); + toastElement.classList.add("bg-secondary"); + }, + modify: function (id, newText) { + var toast = document.querySelector('[data-toast-id="' + id + '"]'); + + if (toast) { + var toastMessage = toast.lastChild; + if (toastMessage) { + toastMessage.innerHTML = newText; + } + } + }, + remove: function (id) { + var toast = document.querySelector('[data-toast-id="' + id + '"]'); + if (toast) { + toast.childNodes.item(1).click(); + } + } + }, + utils: { + scrollToElement: function (id) + { + let e = document.getElementById(id); + e.scrollTop = e.scrollHeight; + }, + triggerResizeEvent: function () + { + window.dispatchEvent(new Event('resize')); + }, + showNotification: function (title, text, img) { + let notification = new Notification(title, { body: text, icon: img }); + } + }, + loading: { + registerXterm: function() + { + console.log("Registering xterm addons"); + + window.XtermBlazor.registerAddon("xterm-addon-fit", new window.FitAddon.FitAddon()); + window.XtermBlazor.registerAddon("xterm-addon-search", new window.SearchAddon.SearchAddon()); + window.XtermBlazor.registerAddon("xterm-addon-web-links", new window.WebLinksAddon.WebLinksAddon()); + }, + loadMonaco: function () + { + console.log("Loading monaco"); + + monaco.editor.defineTheme('moonlight-theme', { + base: 'vs-dark', + inherit: true, + rules: [ + ], + colors: { + 'editor.background': '#000000' + } + }); + } + }, + flashbang: { + run: function() + { + const light = document.getElementById("flashbang"); + light.style.boxShadow = "0 0 10000px 10000px white, 0 0 250px 10px #FFFFFF"; + light.style.animation = "flashbang 5s linear forwards"; + light.onanimationend = moonlight.flashbang.clean; + }, + clean: function() + { + const light = document.getElementById("flashbang"); + light.style.animation = ""; + light.style.opacity = "0"; + } } - } -}; \ No newline at end of file + }; \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/recaptcha.js b/Moonlight/wwwroot/assets/js/recaptcha.js deleted file mode 100644 index b19f5fcc..00000000 --- a/Moonlight/wwwroot/assets/js/recaptcha.js +++ /dev/null @@ -1,9 +0,0 @@ -window.recaptcha = new Object(); -window.recaptcha.render = function (id, sitekey, page) -{ - return grecaptcha.render(id, { - 'sitekey': sitekey, - 'callback': (response) => { page.invokeMethodAsync('CallbackOnSuccess', response); }, - 'expired-callback': () => { page.invokeMethodAsync('CallbackOnExpired'); } - }); -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/snow.js b/Moonlight/wwwroot/assets/js/snow.js deleted file mode 100644 index 389e86e9..00000000 --- a/Moonlight/wwwroot/assets/js/snow.js +++ /dev/null @@ -1,118 +0,0 @@ -window.createSnow = function () { - (function () { - var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || - function (callback) { - window.setTimeout(callback, 1000 / 60); - }; - window.requestAnimationFrame = requestAnimationFrame; - })(); - - - var flakes = [], - canvas = document.getElementById("snow"), - ctx = canvas.getContext("2d"), - flakeCount = 200, - mX = -100, - mY = -100 - - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - - function snow() { - ctx.clearRect(0, 0, canvas.width, canvas.height); - - for (var i = 0; i < flakeCount; i++) { - var flake = flakes[i], - x = mX, - y = mY, - minDist = 150, - x2 = flake.x, - y2 = flake.y; - - var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)), - dx = x2 - x, - dy = y2 - y; - - if (dist < minDist) { - var force = minDist / (dist * dist), - xcomp = (x - x2) / dist, - ycomp = (y - y2) / dist, - deltaV = force / 2; - - flake.velX -= deltaV * xcomp; - flake.velY -= deltaV * ycomp; - - } else { - flake.velX *= .98; - if (flake.velY <= flake.speed) { - flake.velY = flake.speed - } - flake.velX += Math.cos(flake.step += .05) * flake.stepSize; - } - - ctx.fillStyle = "rgba(255,255,255," + flake.opacity + ")"; - flake.y += flake.velY; - flake.x += flake.velX; - - if (flake.y >= canvas.height || flake.y <= 0) { - reset(flake); - } - - - if (flake.x >= canvas.width || flake.x <= 0) { - reset(flake); - } - - ctx.beginPath(); - ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2); - ctx.fill(); - } - requestAnimationFrame(snow); - }; - - function reset(flake) { - flake.x = Math.floor(Math.random() * canvas.width); - flake.y = 0; - flake.size = (Math.random() * 3) + 2; - flake.speed = (Math.random() * 1) + 0.5; - flake.velY = flake.speed; - flake.velX = 0; - flake.opacity = (Math.random() * 0.5) + 0.3; - } - - function init() { - for (var i = 0; i < flakeCount; i++) { - var x = Math.floor(Math.random() * canvas.width), - y = Math.floor(Math.random() * canvas.height), - size = (Math.random() * 3) + 2, - speed = (Math.random() * 1) + 0.5, - opacity = (Math.random() * 0.5) + 0.3; - - flakes.push({ - speed: speed, - velY: speed, - velX: 0, - x: x, - y: y, - size: size, - stepSize: (Math.random()) / 30, - step: 0, - opacity: opacity - }); - } - - snow(); - }; - - canvas.addEventListener("mousemove", function (e) { - mX = e.clientX, - mY = e.clientY - }); - - window.addEventListener("resize", function () { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - }) - - init(); -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/toastUtils.js b/Moonlight/wwwroot/assets/js/toastUtils.js deleted file mode 100644 index cbccb040..00000000 --- a/Moonlight/wwwroot/assets/js/toastUtils.js +++ /dev/null @@ -1,52 +0,0 @@ -window.showInfoToast = function (msg) { - toastr['info'](msg); -} - -window.showErrorToast = function (msg) { - toastr['error'](msg); -} - -window.showSuccessToast = function (msg) { - toastr['success'](msg); -} - -window.showWarningToast = function (msg) { - toastr['warning'](msg); -} - -window.createToast = function (id, text) { - var toast = toastr.success(text, '', - { - closeButton: true, - progressBar: false, - tapToDismiss: false, - timeOut: 0, - extendedTimeOut: 0, - positionClass: "toastr-bottom-right", - preventDuplicates: false, - onclick: function () { - toastr.clear(toast); - } - }); - var toastElement = toast[0]; - toastElement.setAttribute('data-toast-id', id); - toastElement.classList.add("bg-secondary"); -} - -window.modifyToast = function (id, newText) { - var toast = document.querySelector('[data-toast-id="' + id + '"]'); - - if (toast) { - var toastMessage = toast.lastChild; - if (toastMessage) { - toastMessage.innerHTML = newText; - } - } -} - -window.removeToast = function (id) { - var toast = document.querySelector('[data-toast-id="' + id + '"]'); - if (toast) { - toast.childNodes.item(1).click(); - } -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/utils.js b/Moonlight/wwwroot/assets/js/utils.js deleted file mode 100644 index d584c0cd..00000000 --- a/Moonlight/wwwroot/assets/js/utils.js +++ /dev/null @@ -1,42 +0,0 @@ -window.scrollToElement = function (id) -{ - let e = document.getElementById(id); - e.scrollTop = e.scrollHeight; -}; - -window.showModal = function (name) -{ - $('#' + name).modal('show'); -} - -window.hideModal = function (name) -{ - $('#' + name).modal('hide'); -} - -window.attachFileUploadWebkit = function (id) -{ - document.getElementById(id).addEventListener("change", function(e) { - var files = []; - - Array.from(e.target.files).forEach(file => { - files.push(file.webkitRelativePath); - }); - - window.webkitStorage = files; - }); -}; - -window.getWebkitStorage = function () -{ - return window.webkitStorage; -}; - -window.triggerResizeEvent = function () -{ - window.dispatchEvent(new Event('resize')); -} - -window.showNotification = function (title, text, img) { - let notification = new Notification(title, { body: text, icon: img }); -} \ No newline at end of file diff --git a/Moonlight/wwwroot/assets/js/xtermAddons.js b/Moonlight/wwwroot/assets/js/xtermAddons.js deleted file mode 100644 index c8fe21c7..00000000 --- a/Moonlight/wwwroot/assets/js/xtermAddons.js +++ /dev/null @@ -1,3 +0,0 @@ -window.XtermBlazor.registerAddon("xterm-addon-fit", new window.FitAddon.FitAddon()); -window.XtermBlazor.registerAddon("xterm-addon-search", new window.SearchAddon.SearchAddon()); -window.XtermBlazor.registerAddon("xterm-addon-web-links", new window.WebLinksAddon.WebLinksAddon()); \ No newline at end of file From 7bfe13214ad6d08772c5131a6e88760aae7aa517 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 00:17:42 +0200 Subject: [PATCH 06/48] Update _Layout.cshtml --- Moonlight/Pages/_Layout.cshtml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index 74bbf010..a5d7476b 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -100,31 +100,12 @@ -@* - -*@ - -@* - -*@ - -@* - - - - - - - - - -*@ From 473eabf0f85bc25ae76ba79357e594af43da7950 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 00:42:38 +0200 Subject: [PATCH 07/48] Added experimental blazor hub options --- Moonlight/Program.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 76e01893..4ecb04ee 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -41,7 +41,13 @@ namespace Moonlight // Add services to the container. builder.Services.AddRazorPages(); - builder.Services.AddServerSideBlazor(); + builder.Services.AddServerSideBlazor() + .AddHubOptions(options => + { + options.MaximumReceiveMessageSize = 10000000; + options.ClientTimeoutInterval = TimeSpan.FromSeconds(30); + options.HandshakeTimeout = TimeSpan.FromSeconds(10); + }); builder.Services.AddHttpContextAccessor(); // Databases From 73a3ea85ad143606f698b0f548900e6bd19eb127 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 02:44:50 +0200 Subject: [PATCH 08/48] Optimized allocation search. Added sql command log interception --- Moonlight/App/Database/DataContext.cs | 4 ++ .../Interceptors/SqlLoggingInterceptor.cs | 40 +++++++++++++++++++ .../Repositories/NodeAllocationRepository.cs | 28 +++++++++++++ Moonlight/App/Services/ConfigService.cs | 9 +++++ Moonlight/App/Services/ServerService.cs | 34 ++++++---------- Moonlight/App/Services/SubscriptionService.cs | 2 +- Moonlight/Program.cs | 3 +- Moonlight/Properties/launchSettings.json | 11 +++-- Moonlight/Shared/Views/Servers/Index.razor | 1 + 9 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs create mode 100644 Moonlight/App/Repositories/NodeAllocationRepository.cs diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index dc3e77de..7ebac0c5 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -2,6 +2,7 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Database.Entities.LogsEntries; using Moonlight.App.Database.Entities.Notification; +using Moonlight.App.Database.Interceptors; using Moonlight.App.Models.Misc; using Moonlight.App.Services; @@ -65,6 +66,9 @@ public class DataContext : DbContext builder.EnableRetryOnFailure(5); } ); + + if(ConfigService.SqlDebugMode) + optionsBuilder.AddInterceptors(new SqlLoggingInterceptor()); } } } \ No newline at end of file diff --git a/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs b/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs new file mode 100644 index 00000000..06e51413 --- /dev/null +++ b/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs @@ -0,0 +1,40 @@ +using System.Data.Common; +using Logging.Net; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Moonlight.App.Database.Interceptors; + +public class SqlLoggingInterceptor : DbCommandInterceptor +{ + public override InterceptionResult ReaderExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.ReaderExecuting(command, eventData, result); + } + + public override InterceptionResult ScalarExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.ScalarExecuting(command, eventData, result); + } + + public override InterceptionResult NonQueryExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.NonQueryExecuting(command, eventData, result); + } + + private void LogSql(string sql) + { + Logger.Info($"[SQL DEBUG] {sql.Replace("\n", "")}"); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/NodeAllocationRepository.cs b/Moonlight/App/Repositories/NodeAllocationRepository.cs new file mode 100644 index 00000000..529c9fbc --- /dev/null +++ b/Moonlight/App/Repositories/NodeAllocationRepository.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class NodeAllocationRepository : IDisposable +{ + // This repository is ONLY for the server creation service, so allocations can be found + // using raw sql. DO NOT use this in any other component + + private readonly DataContext DataContext; + + public NodeAllocationRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.NodeAllocations; + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index 27bc5bc7..6aa11c43 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -12,6 +12,7 @@ public class ConfigService : IConfiguration private IConfiguration Configuration; public bool DebugMode { get; private set; } = false; + public bool SqlDebugMode { get; private set; } = false; public ConfigService(StorageService storageService) { @@ -28,6 +29,14 @@ public class ConfigService : IConfiguration if (DebugMode) Logger.Debug("Debug mode enabled"); + + var sqlDebugVar = Environment.GetEnvironmentVariable("ML_SQL_DEBUG"); + + if (sqlDebugVar != null) + SqlDebugMode = bool.Parse(sqlDebugVar); + + if (SqlDebugMode) + Logger.Debug("Sql debug mode enabled"); } public void Reload() diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 14f53944..1ccf204d 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -21,6 +21,7 @@ public class ServerService private readonly UserRepository UserRepository; private readonly ImageRepository ImageRepository; private readonly NodeRepository NodeRepository; + private readonly NodeAllocationRepository NodeAllocationRepository; private readonly WingsApiHelper WingsApiHelper; private readonly MessageService MessageService; private readonly UserService UserService; @@ -44,7 +45,8 @@ public class ServerService SecurityLogService securityLogService, AuditLogService auditLogService, ErrorLogService errorLogService, - NodeService nodeService) + NodeService nodeService, + NodeAllocationRepository nodeAllocationRepository) { ServerRepository = serverRepository; WingsApiHelper = wingsApiHelper; @@ -59,6 +61,7 @@ public class ServerService AuditLogService = auditLogService; ErrorLogService = errorLogService; NodeService = nodeService; + NodeAllocationRepository = nodeAllocationRepository; } private Server EnsureNodeData(Server s) @@ -268,32 +271,19 @@ public class ServerService .Include(x => x.DockerImages) .First(x => x.Id == i.Id); - Node node; - - if (n == null) - { - node = NodeRepository - .Get() - .Include(x => x.Allocations) - .First(); //TODO: Add smart deploy maybe - } - else - { - node = NodeRepository - .Get() - .Include(x => x.Allocations) - .First(x => x.Id == n.Id); - } + Node node = n ?? NodeRepository.Get().First(); NodeAllocation[] freeAllocations; try { - freeAllocations = node.Allocations - .Where(a => !ServerRepository.Get() - .SelectMany(s => s.Allocations) - .Any(b => b.Id == a.Id)) - .Take(allocations).ToArray(); + // We have sadly no choice to use entity framework to do what the sql call does, there + // are only slower ways, so we will use a raw sql call as a exception + + freeAllocations = NodeAllocationRepository + .Get() + .FromSqlRaw($"SELECT * FROM `NodeAllocations` WHERE ServerId IS NULL AND NodeId={node.Id} LIMIT {allocations}") + .ToArray(); } catch (Exception) { diff --git a/Moonlight/App/Services/SubscriptionService.cs b/Moonlight/App/Services/SubscriptionService.cs index 95c190da..2dcc797c 100644 --- a/Moonlight/App/Services/SubscriptionService.cs +++ b/Moonlight/App/Services/SubscriptionService.cs @@ -89,7 +89,7 @@ public class SubscriptionService } } - public async Task GetLimit(string identifier) + public async Task GetLimit(string identifier) // Cache, optimize sql code { var subscription = await GetCurrent(); var defaultLimits = await GetDefaultLimits(); diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 4ecb04ee..ffd7894f 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -71,9 +71,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Properties/launchSettings.json b/Moonlight/Properties/launchSettings.json index acd90c6c..237a6320 100644 --- a/Moonlight/Properties/launchSettings.json +++ b/Moonlight/Properties/launchSettings.json @@ -17,12 +17,15 @@ "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118", "dotnetRunMessages": true }, - "IIS Express": { - "commandName": "IISExpress", + "Sql Debug": { + "commandName": "Project", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "ASPNETCORE_ENVIRONMENT": "Development", + "ML_SQL_DEBUG": "true" + }, + "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118", + "dotnetRunMessages": true }, "Docker": { "commandName": "Docker", diff --git a/Moonlight/Shared/Views/Servers/Index.razor b/Moonlight/Shared/Views/Servers/Index.razor index d2047485..b0131db7 100644 --- a/Moonlight/Shared/Views/Servers/Index.razor +++ b/Moonlight/Shared/Views/Servers/Index.razor @@ -103,6 +103,7 @@ .Include(x => x.Node) .Include(x => x.Image) .Where(x => x.Owner.Id == User.Id) + .OrderBy(x => x.Name) .ToArray(); foreach (var server in AllServers) From f8c3a5ddb7beaa5e98f067045ba4e5141916c0c1 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 14:57:54 +0200 Subject: [PATCH 09/48] Fixed xterm addons --- Moonlight/Pages/_Layout.cshtml | 8 ++++++-- Moonlight/Shared/Layouts/MainLayout.razor | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index a5d7476b..ca9c2287 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -54,7 +54,7 @@ - + Loading @(loadingMessage.Message) @@ -108,6 +108,10 @@ + + diff --git a/Moonlight/Shared/Layouts/MainLayout.razor b/Moonlight/Shared/Layouts/MainLayout.razor index 064fd5c7..cc7d02d1 100644 --- a/Moonlight/Shared/Layouts/MainLayout.razor +++ b/Moonlight/Shared/Layouts/MainLayout.razor @@ -160,8 +160,6 @@ await JsRuntime.InvokeVoidAsync("KTMenu.createInstances"); await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances"); - await JsRuntime.InvokeVoidAsync("moonlight.loading.registerXterm"); - await SessionService.Register(); NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); }; From a0fecfdec970a53bebf11cea8d8c37c53ee0fd0f Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 15:14:17 +0200 Subject: [PATCH 10/48] Update Changelog.razor --- Moonlight/Shared/Views/Changelog.razor | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Moonlight/Shared/Views/Changelog.razor b/Moonlight/Shared/Views/Changelog.razor index ad68c1e2..521a15d3 100644 --- a/Moonlight/Shared/Views/Changelog.razor +++ b/Moonlight/Shared/Views/Changelog.razor @@ -1,11 +1,25 @@ @page "/changelog" @{ - List changelog = new List() + List changelog = new List { - new[] {"title", "body1", "body2"}, - new[] {"title2", "body3", "body4", "body5"}, + new[] + { + "Patch 1, 16.04.2023", + "Loading messages have been implemented", + "Default subscription config has been implemented", + "Blazor hub has been optimized for large amount of users", + "Backup error handling improved", + "Client side javascript sorted", + "Sorted user server list implemented" + }, + new[] + { + "Patch 2, 16.04.2023", + "Fixed xterm addons" + } }; + int i = 0; } From 0a244ac4749c3f0da3b6e96da45a6a390864f1a1 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 16:01:19 +0200 Subject: [PATCH 11/48] Fixed my stupid mistakes --- Moonlight/App/Services/ServerService.cs | 4 +- Moonlight/Shared/Views/Admin/Nodes/Edit.razor | 42 +++++++++++++++++++ Moonlight/Shared/Views/Servers/Create.razor | 6 +-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 1ccf204d..ae724ac9 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -259,7 +259,7 @@ public class ServerService } public async Task Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null, - Action? modifyDetails = null, int allocations = 1) + Action? modifyDetails = null) { var user = UserRepository .Get() @@ -271,6 +271,8 @@ public class ServerService .Include(x => x.DockerImages) .First(x => x.Id == i.Id); + var allocations = image.Allocations; + Node node = n ?? NodeRepository.Get().First(); NodeAllocation[] freeAllocations; diff --git a/Moonlight/Shared/Views/Admin/Nodes/Edit.razor b/Moonlight/Shared/Views/Admin/Nodes/Edit.razor index b9583492..21b7a9cc 100644 --- a/Moonlight/Shared/Views/Admin/Nodes/Edit.razor +++ b/Moonlight/Shared/Views/Admin/Nodes/Edit.razor @@ -109,6 +109,25 @@ OnClick="CreateAllocation"> +
+ + +
+
+ + +
+
+ + +
@@ -142,6 +161,9 @@ private int Port = 2000; + private int StartPort = 2000; + private int EndPort = 3000; + private Task Load(LazyLoader arg) { Node = NodeRepository @@ -183,4 +205,24 @@ await LazyLoader.Reload(); } + + private async Task CreateAllocationRange() + { + for (int i = StartPort; i < EndPort; i++) + { + var nodeAllocation = new NodeAllocation() + { + Port = i + }; + + Node!.Allocations.Add(nodeAllocation); + } + + NodeRepository.Update(Node); + + StartPort = 2000; + EndPort = 3000; + + await LazyLoader.Reload(); + } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Servers/Create.razor b/Moonlight/Shared/Views/Servers/Create.razor index b6c6c318..30f687d6 100644 --- a/Moonlight/Shared/Views/Servers/Create.razor +++ b/Moonlight/Shared/Views/Servers/Create.razor @@ -209,10 +209,8 @@ disk, User, Model.Image, - DeployNode, - null, - Model.Image.Allocations - ); + DeployNode + ); NavigationManager.NavigateTo($"/server/{server.Uuid}"); } From 65b4f36a011f28825d412d0e14f23f833a6364ca Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 16:06:08 +0200 Subject: [PATCH 12/48] Update Changelog.razor --- Moonlight/Shared/Views/Changelog.razor | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Moonlight/Shared/Views/Changelog.razor b/Moonlight/Shared/Views/Changelog.razor index 521a15d3..59ef0a2d 100644 --- a/Moonlight/Shared/Views/Changelog.razor +++ b/Moonlight/Shared/Views/Changelog.razor @@ -17,6 +17,11 @@ { "Patch 2, 16.04.2023", "Fixed xterm addons" + }, + new[] + { + "Patch 3, 16.04.2023", + "Fixed server allocation bug" } }; From fa09674c06364235350346436a20ef2e0bf9ab45 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sun, 16 Apr 2023 19:28:12 +0200 Subject: [PATCH 13/48] Implemented file view loading animation --- .../FileManagerPartials/FileView.razor | 157 +++++++++--------- .../Components/Partials/ContentBlock.razor | 52 ++++++ 2 files changed, 135 insertions(+), 74 deletions(-) create mode 100644 Moonlight/Shared/Components/Partials/ContentBlock.razor diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor index dcf0a346..0e889e8a 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor @@ -35,88 +35,90 @@
- - - - - - - @foreach (var file in Data) - { + - + - } + @foreach (var file in Data) + { + + + + + + + } + @@ -138,12 +140,13 @@ @code { + [Parameter] public FileAccess Access { get; set; } [Parameter] public Func>? OnElementClicked { get; set; } - + [Parameter] public Func? OnSelectionChanged { get; set; } @@ -155,7 +158,7 @@ [Parameter] public bool DisableScrolling { get; set; } = false; - + [Parameter] public Func? Filter { get; set; } @@ -169,15 +172,19 @@ private Dictionary ToggleStatusCache = new(); private bool AllToggled = false; + private ContentBlock ContentBlock; + public async Task Refresh() { + ContentBlock?.SetBlocking(true); + var list = new List(); - + foreach (var fileData in await Access.Ls()) { if (Filter != null) { - if(Filter.Invoke(fileData)) + if (Filter.Invoke(fileData)) list.Add(fileData); } else @@ -185,7 +192,7 @@ } Data = list.ToArray(); - + ToggleStatusCache.Clear(); AllToggled = false; @@ -196,6 +203,8 @@ await InvokeAsync(StateHasChanged); OnSelectionChanged?.Invoke(); + + ContentBlock?.SetBlocking(false); } private async Task Load(LazyLoader arg) @@ -257,7 +266,7 @@ if (canceled) return; } - + await Access.Up(); await Refresh(); } @@ -273,4 +282,4 @@ return Task.CompletedTask; } -} \ No newline at end of file + } \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/ContentBlock.razor b/Moonlight/Shared/Components/Partials/ContentBlock.razor new file mode 100644 index 00000000..c987da71 --- /dev/null +++ b/Moonlight/Shared/Components/Partials/ContentBlock.razor @@ -0,0 +1,52 @@ +@if (AllowContentOverride) +{ + if (IsBlocking) + { +
+
+ @(ChildContent) +
+
+
+
+
+
+ } + else + { + @ChildContent + } +} +else +{ +
+
+ @(ChildContent) +
+ @if (IsBlocking) + { +
+
+
+
+ } +
+} + +@code +{ + [Parameter] + public RenderFragment ChildContent { get; set; } + + [Parameter] + public bool AllowContentOverride { get; set; } = false; + + private bool IsBlocking = false; + + public async Task SetBlocking(bool b) + { + IsBlocking = b; + + await InvokeAsync(StateHasChanged); + } +} \ No newline at end of file From fd008e56aa4bf4e2af6c823001e236ad55ed129c Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 19 Apr 2023 21:04:40 +0200 Subject: [PATCH 14/48] Added new file manager, renamed websites to webspaces. Added cloudpanel integration partialy. Added generic repos and more stuff --- .../CloudPanel/CloudPanelApiHelper.cs | 83 ++ .../CloudPanel/CloudPanelException.cs | 32 + .../CloudPanel/Requests/AddPhpSite.cs | 16 + Moonlight/App/Database/DataContext.cs | 4 + Moonlight/App/Database/Entities/CloudPanel.cs | 10 + .../App/Database/Entities/MySqlDatabase.cs | 9 + Moonlight/App/Database/Entities/WebSpace.cs | 13 + ...19120719_AddedCloudPanelModels.Designer.cs | 1099 ++++++++++++++++ .../20230419120719_AddedCloudPanelModels.cs | 121 ++ ...ddedHostFieldToCloudPanelModel.Designer.cs | 1103 +++++++++++++++++ ...9125155_AddedHostFieldToCloudPanelModel.cs | 29 + .../Migrations/DataContextModelSnapshot.cs | 123 ++ Moonlight/App/Helpers/Files/SftpFileAccess.cs | 206 +++ Moonlight/App/Helpers/SyncStreamAdapter.cs | 58 + .../App/Models/Forms/CloudPanelDataModel.cs | 19 + Moonlight/App/Repositories/Repository.cs | 40 + Moonlight/App/Services/SmartDeployService.cs | 11 +- .../Statistics/StatisticsCaptureService.cs | 6 +- Moonlight/App/Services/WebSpaceService.cs | 166 +++ Moonlight/App/Services/WebsiteService.cs | 383 ------ Moonlight/Moonlight.csproj | 2 + Moonlight/Program.cs | 5 +- .../FileManagerPartials/FilePath.razor | 1 + ...n.razor => AdminWebspacesNavigation.razor} | 8 +- ...ashboard.razor => WebSpaceDashboard.razor} | 12 +- ...atabases.razor => WebSpaceDatabases.razor} | 18 +- ...WebsiteFiles.razor => WebSpaceFiles.razor} | 6 +- .../WebsiteControl/WebSpaceFtp.razor | 55 + ...igation.razor => WebSpaceNavigation.razor} | 14 +- .../WebsiteControl/WebsiteFtp.razor | 64 - Moonlight/Shared/Views/Admin/Index.razor | 2 +- .../Views/Admin/Websites/Servers/Edit.razor | 98 -- .../Admin/{Websites => Webspaces}/Index.razor | 45 +- .../Admin/{Websites => Webspaces}/New.razor | 8 +- .../Views/Admin/Webspaces/Servers/Edit.razor | 102 ++ .../Servers/Index.razor | 49 +- .../{Websites => Webspaces}/Servers/New.razor | 25 +- .../Views/{Website => Webspace}/Index.razor | 44 +- .../{Websites => Webspaces}/Create.razor | 8 +- .../Views/{Websites => Webspaces}/Index.razor | 24 +- 40 files changed, 3430 insertions(+), 691 deletions(-) create mode 100644 Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs create mode 100644 Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs create mode 100644 Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs create mode 100644 Moonlight/App/Database/Entities/CloudPanel.cs create mode 100644 Moonlight/App/Database/Entities/MySqlDatabase.cs create mode 100644 Moonlight/App/Database/Entities/WebSpace.cs create mode 100644 Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.cs create mode 100644 Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.cs create mode 100644 Moonlight/App/Helpers/Files/SftpFileAccess.cs create mode 100644 Moonlight/App/Helpers/SyncStreamAdapter.cs create mode 100644 Moonlight/App/Models/Forms/CloudPanelDataModel.cs create mode 100644 Moonlight/App/Repositories/Repository.cs create mode 100644 Moonlight/App/Services/WebSpaceService.cs delete mode 100644 Moonlight/App/Services/WebsiteService.cs rename Moonlight/Shared/Components/Navigations/{AdminWebsitesNavigation.razor => AdminWebspacesNavigation.razor} (72%) rename Moonlight/Shared/Components/WebsiteControl/{WebsiteDashboard.razor => WebSpaceDashboard.razor} (92%) rename Moonlight/Shared/Components/WebsiteControl/{WebsiteDatabases.razor => WebSpaceDatabases.razor} (90%) rename Moonlight/Shared/Components/WebsiteControl/{WebsiteFiles.razor => WebSpaceFiles.razor} (69%) create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor rename Moonlight/Shared/Components/WebsiteControl/{WebsiteNavigation.razor => WebSpaceNavigation.razor} (76%) delete mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteFtp.razor delete mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor rename Moonlight/Shared/Views/Admin/{Websites => Webspaces}/Index.razor (53%) rename Moonlight/Shared/Views/Admin/{Websites => Webspaces}/New.razor (89%) create mode 100644 Moonlight/Shared/Views/Admin/Webspaces/Servers/Edit.razor rename Moonlight/Shared/Views/Admin/{Websites => Webspaces}/Servers/Index.razor (54%) rename Moonlight/Shared/Views/Admin/{Websites => Webspaces}/Servers/New.razor (69%) rename Moonlight/Shared/Views/{Website => Webspace}/Index.razor (74%) rename Moonlight/Shared/Views/{Websites => Webspaces}/Create.razor (95%) rename Moonlight/Shared/Views/{Websites => Webspaces}/Index.razor (75%) diff --git a/Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs b/Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs new file mode 100644 index 00000000..fd6e99fb --- /dev/null +++ b/Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs @@ -0,0 +1,83 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Plesk.Resources; +using Newtonsoft.Json; +using RestSharp; + +namespace Moonlight.App.ApiClients.CloudPanel; + +public class CloudPanelApiHelper +{ + private readonly RestClient Client; + + public CloudPanelApiHelper() + { + Client = new(); + } + + public async Task Post(Database.Entities.CloudPanel cloudPanel, string resource, object? body) + { + var request = CreateRequest(cloudPanel, resource); + + request.Method = Method.Post; + + if(body != null) + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new CloudPanelException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + public async Task Delete(Database.Entities.CloudPanel cloudPanel, string resource, object? body) + { + var request = CreateRequest(cloudPanel, resource); + + request.Method = Method.Delete; + + if(body != null) + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new CloudPanelException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + private RestRequest CreateRequest(Database.Entities.CloudPanel cloudPanel, string resource) + { + var url = $"{cloudPanel.ApiUrl}/" + resource; + + var request = new RestRequest(url); + + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json"); + request.AddHeader("Authorization", "Bearer " + cloudPanel.ApiKey); + + return request; + } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs b/Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs new file mode 100644 index 00000000..a7610348 --- /dev/null +++ b/Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Moonlight.App.ApiClients.CloudPanel; + +[Serializable] +public class CloudPanelException : Exception +{ + public int StatusCode { get; set; } + + public CloudPanelException() + { + } + + public CloudPanelException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public CloudPanelException(string message) : base(message) + { + } + + public CloudPanelException(string message, Exception inner) : base(message, inner) + { + } + + protected CloudPanelException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs b/Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs new file mode 100644 index 00000000..9e04eeff --- /dev/null +++ b/Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.ApiClients.CloudPanel.Requests; + +public class AddPhpSite +{ + [JsonProperty("domainName")] public string DomainName { get; set; } = ""; + + [JsonProperty("siteUser")] public string SiteUser { get; set; } = ""; + + [JsonProperty("siteUserPassword")] public string SiteUserPassword { get; set; } = ""; + + [JsonProperty("vHostTemplate")] public string VHostTemplate { get; set; } = ""; + + [JsonProperty("phpVersion")] public string PhpVersion { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 7ebac0c5..aeaa61b1 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -43,6 +43,10 @@ public class DataContext : DbContext public DbSet Websites { get; set; } public DbSet Statistics { get; set; } public DbSet NewsEntries { get; set; } + + public DbSet CloudPanels { get; set; } + public DbSet Databases { get; set; } + public DbSet WebSpaces { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/CloudPanel.cs b/Moonlight/App/Database/Entities/CloudPanel.cs new file mode 100644 index 00000000..de872b0f --- /dev/null +++ b/Moonlight/App/Database/Entities/CloudPanel.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Database.Entities; + +public class CloudPanel +{ + public int Id { get; set; } + public string Name { get; set; } = ""; + public string ApiUrl { get; set; } = ""; + public string ApiKey { get; set; } = ""; + public string Host { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/MySqlDatabase.cs b/Moonlight/App/Database/Entities/MySqlDatabase.cs new file mode 100644 index 00000000..c9cdc6ef --- /dev/null +++ b/Moonlight/App/Database/Entities/MySqlDatabase.cs @@ -0,0 +1,9 @@ +namespace Moonlight.App.Database.Entities; + +public class MySqlDatabase +{ + public int Id { get; set; } + public WebSpace WebSpace { get; set; } + public string UserName { get; set; } = ""; + public string Password { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/WebSpace.cs b/Moonlight/App/Database/Entities/WebSpace.cs new file mode 100644 index 00000000..1112fdf6 --- /dev/null +++ b/Moonlight/App/Database/Entities/WebSpace.cs @@ -0,0 +1,13 @@ +namespace Moonlight.App.Database.Entities; + +public class WebSpace +{ + public int Id { get; set; } + public string Domain { get; set; } = ""; + public string UserName { get; set; } = ""; + public string Password { get; set; } = ""; + public string VHostTemplate { get; set; } = ""; + public User Owner { get; set; } + public List Databases { get; set; } = new(); + public CloudPanel CloudPanel { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs b/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs new file mode 100644 index 00000000..26edf52f --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs @@ -0,0 +1,1099 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230419120719_AddedCloudPanelModels")] + partial class AddedCloudPanelModels + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("CloudPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebSpaceId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebSpaceId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("ServerId"); + + b.ToTable("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("IsCleanupException") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudPanelId") + .HasColumnType("int"); + + b.Property("Domain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CloudPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("WebSpaces"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace") + .WithMany("Databases") + .HasForeignKey("WebSpaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebSpace"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId"); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId"); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel") + .WithMany() + .HasForeignKey("CloudPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CloudPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Navigation("Databases"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.cs b/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.cs new file mode 100644 index 00000000..845437af --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.cs @@ -0,0 +1,121 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedCloudPanelModels : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CloudPanels", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiUrl = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiKey = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_CloudPanels", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WebSpaces", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Domain = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Password = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + VHostTemplate = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + OwnerId = table.Column(type: "int", nullable: false), + CloudPanelId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WebSpaces", x => x.Id); + table.ForeignKey( + name: "FK_WebSpaces_CloudPanels_CloudPanelId", + column: x => x.CloudPanelId, + principalTable: "CloudPanels", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_WebSpaces_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Databases", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + WebSpaceId = table.Column(type: "int", nullable: false), + UserName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Password = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Databases", x => x.Id); + table.ForeignKey( + name: "FK_Databases_WebSpaces_WebSpaceId", + column: x => x.WebSpaceId, + principalTable: "WebSpaces", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Databases_WebSpaceId", + table: "Databases", + column: "WebSpaceId"); + + migrationBuilder.CreateIndex( + name: "IX_WebSpaces_CloudPanelId", + table: "WebSpaces", + column: "CloudPanelId"); + + migrationBuilder.CreateIndex( + name: "IX_WebSpaces_OwnerId", + table: "WebSpaces", + column: "OwnerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Databases"); + + migrationBuilder.DropTable( + name: "WebSpaces"); + + migrationBuilder.DropTable( + name: "CloudPanels"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs b/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs new file mode 100644 index 00000000..dda0a9f9 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs @@ -0,0 +1,1103 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230419125155_AddedHostFieldToCloudPanelModel")] + partial class AddedHostFieldToCloudPanelModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Host") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("CloudPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebSpaceId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebSpaceId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("ServerId"); + + b.ToTable("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("IsCleanupException") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudPanelId") + .HasColumnType("int"); + + b.Property("Domain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CloudPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("WebSpaces"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace") + .WithMany("Databases") + .HasForeignKey("WebSpaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebSpace"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId"); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId"); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel") + .WithMany() + .HasForeignKey("CloudPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CloudPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Navigation("Databases"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.cs b/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.cs new file mode 100644 index 00000000..a0273c4d --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedHostFieldToCloudPanelModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Host", + table: "CloudPanels", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Host", + table: "CloudPanels"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 7a0da1a7..88d74810 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -19,6 +19,33 @@ namespace Moonlight.App.Database.Migrations .HasAnnotation("ProductVersion", "7.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Host") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("CloudPanels"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => { b.Property("Id") @@ -281,6 +308,30 @@ namespace Moonlight.App.Database.Migrations b.ToTable("SecurityLog"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WebSpaceId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("WebSpaceId"); + + b.ToTable("Databases"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => { b.Property("Id") @@ -748,6 +799,43 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudPanelId") + .HasColumnType("int"); + + b.Property("Domain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CloudPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("WebSpaces"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => { b.Property("Id") @@ -828,6 +916,17 @@ namespace Moonlight.App.Database.Migrations .HasForeignKey("ImageId"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b => + { + b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace") + .WithMany("Databases") + .HasForeignKey("WebSpaceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WebSpace"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => { b.HasOne("Moonlight.App.Database.Entities.Node", null) @@ -932,6 +1031,25 @@ namespace Moonlight.App.Database.Migrations b.Navigation("CurrentSubscription"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel") + .WithMany() + .HasForeignKey("CloudPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CloudPanel"); + + b.Navigation("Owner"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => { b.HasOne("Moonlight.App.Database.Entities.User", "Owner") @@ -971,6 +1089,11 @@ namespace Moonlight.App.Database.Migrations b.Navigation("Variables"); }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => + { + b.Navigation("Databases"); + }); #pragma warning restore 612, 618 } } diff --git a/Moonlight/App/Helpers/Files/SftpFileAccess.cs b/Moonlight/App/Helpers/Files/SftpFileAccess.cs new file mode 100644 index 00000000..f79b4388 --- /dev/null +++ b/Moonlight/App/Helpers/Files/SftpFileAccess.cs @@ -0,0 +1,206 @@ +using Logging.Net; +using Renci.SshNet; +using ConnectionInfo = Renci.SshNet.ConnectionInfo; + +namespace Moonlight.App.Helpers.Files; + +public class SftpFileAccess : FileAccess +{ + private readonly string SftpHost; + private readonly string SftpUser; + private readonly string SftpPassword; + private readonly int SftpPort; + private readonly bool ForceUserDir; + + private readonly SftpClient Client; + + private string InternalPath + { + get + { + if (ForceUserDir) + return $"/home/{SftpUser}{CurrentPath}"; + + return InternalPath; + } + } + + public SftpFileAccess(string sftpHost, string sftpUser, string sftpPassword, int sftpPort, + bool forceUserDir = false) + { + SftpHost = sftpHost; + SftpUser = sftpUser; + SftpPassword = sftpPassword; + SftpPort = sftpPort; + ForceUserDir = forceUserDir; + + Client = new( + new ConnectionInfo( + SftpHost, + SftpPort, + SftpUser, + new PasswordAuthenticationMethod( + SftpUser, + SftpPassword + ) + ) + ); + } + + private void EnsureConnect() + { + if (!Client.IsConnected) + Client.Connect(); + } + + + public override Task Ls() + { + EnsureConnect(); + + var x = new List(); + + foreach (var file in Client.ListDirectory(InternalPath)) + { + if (file.Name != "." && file.Name != "..") + { + x.Add(new() + { + Name = file.Name, + Size = file.Attributes.Size, + IsFile = !file.IsDirectory + }); + } + } + + return Task.FromResult(x.ToArray()); + } + + public override Task Cd(string dir) + { + var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/"; + x = x.Replace("//", "/"); + CurrentPath = x; + + return Task.CompletedTask; + } + + public override Task Up() + { + CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", ""); + return Task.CompletedTask; + } + + public override Task SetDir(string dir) + { + CurrentPath = dir; + return Task.CompletedTask; + } + + public override Task Read(FileData fileData) + { + EnsureConnect(); + + var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Open); + + if (textStream == null) + return Task.FromResult(""); + + var streamReader = new StreamReader(textStream); + + var text = streamReader.ReadToEnd(); + + streamReader.Close(); + textStream.Close(); + + return Task.FromResult(text); + } + + public override Task Write(FileData fileData, string content) + { + EnsureConnect(); + + var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Create); + + var streamWriter = new StreamWriter(textStream); + streamWriter.Write(content); + + streamWriter.Flush(); + textStream.Flush(); + + streamWriter.Close(); + textStream.Close(); + + return Task.CompletedTask; + } + + public override async Task Upload(string name, Stream stream, Action? progressUpdated = null) + { + var dataStream = new SyncStreamAdapter(stream); + + await Task.Factory.FromAsync((x, _) => Client.BeginUploadFile(dataStream, InternalPath + name, x, null, u => + { + progressUpdated?.Invoke((int)((long)u / stream.Length)); + }), + Client.EndUploadFile, null); + } + + public override Task MkDir(string name) + { + Client.CreateDirectory(InternalPath + name); + + return Task.CompletedTask; + } + + public override Task Pwd() + { + return Task.FromResult(CurrentPath); + } + + public override Task DownloadUrl(FileData fileData) + { + throw new NotImplementedException(); + } + + public override Task DownloadStream(FileData fileData) + { + var stream = new MemoryStream(100 * 1024 * 1024); + Client.DownloadFile(InternalPath + fileData.Name, stream); + + return Task.FromResult(stream); + } + + public override Task Delete(FileData fileData) + { + Client.Delete(InternalPath + fileData.Name); + + return Task.CompletedTask; + } + + public override Task Move(FileData fileData, string newPath) + { + Client.RenameFile(InternalPath + fileData.Name, InternalPath + newPath); + + return Task.CompletedTask; + } + + public override Task Compress(params FileData[] files) + { + throw new NotImplementedException(); + } + + public override Task Decompress(FileData fileData) + { + throw new NotImplementedException(); + } + + public override Task GetLaunchUrl() + { + return Task.FromResult($"sftp://{SftpUser}@{SftpHost}:{SftpPort}"); + } + + public override object Clone() + { + return new SftpFileAccess(SftpHost, SftpUser, SftpPassword, SftpPort, ForceUserDir); + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/SyncStreamAdapter.cs b/Moonlight/App/Helpers/SyncStreamAdapter.cs new file mode 100644 index 00000000..304b3f12 --- /dev/null +++ b/Moonlight/App/Helpers/SyncStreamAdapter.cs @@ -0,0 +1,58 @@ +namespace Moonlight.App.Helpers; + +public class SyncStreamAdapter : Stream +{ + private readonly Stream _stream; + + public SyncStreamAdapter(Stream stream) + { + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + } + + public override bool CanRead => _stream.CanRead; + public override bool CanSeek => _stream.CanSeek; + public override bool CanWrite => _stream.CanWrite; + public override long Length => _stream.Length; + + public override long Position + { + get => _stream.Position; + set => _stream.Position = value; + } + + public override void Flush() + { + _stream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var task = Task.Run(() => _stream.ReadAsync(buffer, offset, count)); + return task.GetAwaiter().GetResult(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + var task = Task.Run(() => _stream.WriteAsync(buffer, offset, count)); + task.GetAwaiter().GetResult(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _stream?.Dispose(); + } + base.Dispose(disposing); + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/CloudPanelDataModel.cs b/Moonlight/App/Models/Forms/CloudPanelDataModel.cs new file mode 100644 index 00000000..d08e9461 --- /dev/null +++ b/Moonlight/App/Models/Forms/CloudPanelDataModel.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class CloudPanelDataModel +{ + [Required(ErrorMessage = "You have to enter a name")] + [MaxLength(32, ErrorMessage = "The name should not be longer than 32 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "You need to specify the host")] + public string Host { get; set; } + + [Required(ErrorMessage = "You need to enter an api url")] + public string ApiUrl { get; set; } + + [Required(ErrorMessage = "You need to enter an api key")] + public string ApiKey { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/Repository.cs b/Moonlight/App/Repositories/Repository.cs new file mode 100644 index 00000000..1c46f6ab --- /dev/null +++ b/Moonlight/App/Repositories/Repository.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; + +namespace Moonlight.App.Repositories; + +public class Repository where TEntity : class +{ + private readonly DataContext DataContext; + private readonly DbSet DbSet; + + public Repository(DataContext dbContext) + { + DataContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + DbSet = DataContext.Set(); + } + + public DbSet Get() + { + return DbSet; + } + + public TEntity Add(TEntity entity) + { + var x = DbSet.Add(entity); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(TEntity entity) + { + DbSet.Update(entity); + DataContext.SaveChanges(); + } + + public void Delete(TEntity entity) + { + DbSet.Remove(entity); + DataContext.SaveChanges(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs index 57aa4bc8..e5e69f87 100644 --- a/Moonlight/App/Services/SmartDeployService.cs +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -7,17 +7,17 @@ public class SmartDeployService { private readonly NodeRepository NodeRepository; private readonly PleskServerRepository PleskServerRepository; - private readonly WebsiteService WebsiteService; + private readonly WebSpaceService WebSpaceService; private readonly NodeService NodeService; public SmartDeployService( NodeRepository nodeRepository, - NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService) + NodeService nodeService, PleskServerRepository pleskServerRepository, WebSpaceService webSpaceService) { NodeRepository = nodeRepository; NodeService = nodeService; PleskServerRepository = pleskServerRepository; - WebsiteService = websiteService; + WebSpaceService = webSpaceService; } public async Task GetNode() @@ -44,10 +44,7 @@ public class SmartDeployService foreach (var pleskServer in PleskServerRepository.Get().ToArray()) { - if (await WebsiteService.IsHostUp(pleskServer)) - { - result.Add(pleskServer); - } + result.Add(pleskServer); } return result.FirstOrDefault(); diff --git a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs index 3098e5d1..f3a9d59b 100644 --- a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs +++ b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs @@ -9,7 +9,7 @@ public class StatisticsCaptureService private readonly ConfigService ConfigService; private readonly StatisticsRepository StatisticsRepository; private readonly IServiceScopeFactory ServiceScopeFactory; - private readonly WebsiteService WebsiteService; + private readonly WebSpaceService WebSpaceService; private readonly PleskServerRepository PleskServerRepository; private PeriodicTimer Timer; @@ -21,7 +21,7 @@ public class StatisticsCaptureService DataContext = provider.GetRequiredService(); ConfigService = configService; StatisticsRepository = provider.GetRequiredService(); - WebsiteService = provider.GetRequiredService(); + WebSpaceService = provider.GetRequiredService(); PleskServerRepository = provider.GetRequiredService(); var config = ConfigService.GetSection("Moonlight").GetSection("Statistics"); @@ -48,7 +48,7 @@ public class StatisticsCaptureService await foreach (var pleskServer in PleskServerRepository.Get()) { - databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount; + //databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount; } StatisticsRepository.Add("statistics.databasesCount", databases); diff --git a/Moonlight/App/Services/WebSpaceService.cs b/Moonlight/App/Services/WebSpaceService.cs new file mode 100644 index 00000000..549c44dc --- /dev/null +++ b/Moonlight/App/Services/WebSpaceService.cs @@ -0,0 +1,166 @@ +using Logging.Net; +using Microsoft.EntityFrameworkCore; +using Moonlight.App.ApiClients.CloudPanel; +using Moonlight.App.ApiClients.CloudPanel.Requests; +using Moonlight.App.Database.Entities; +using Moonlight.App.Exceptions; +using Moonlight.App.Helpers; +using Moonlight.App.Helpers.Files; +using Moonlight.App.Models.Plesk.Requests; +using Moonlight.App.Models.Plesk.Resources; +using Moonlight.App.Repositories; +using FileAccess = Moonlight.App.Helpers.Files.FileAccess; + +namespace Moonlight.App.Services; + +public class WebSpaceService +{ + private readonly Repository CloudPanelRepository; + private readonly Repository WebSpaceRepository; + private readonly CloudPanelApiHelper CloudPanelApiHelper; + + public WebSpaceService(Repository cloudPanelRepository, Repository webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper) + { + CloudPanelRepository = cloudPanelRepository; + WebSpaceRepository = webSpaceRepository; + CloudPanelApiHelper = cloudPanelApiHelper; + } + + public async Task Create(string domain, User owner, CloudPanel? ps = null) + { + if (WebSpaceRepository.Get().Any(x => x.Domain == domain)) + throw new DisplayException("A website with this domain does already exist"); + + var cloudPanel = ps ?? CloudPanelRepository.Get().First(); + + var ftpLogin = domain.Replace(".", "_"); + var ftpPassword = StringHelper.GenerateString(16); + + var phpVersion = "8.1"; // TODO: Add config option or smth + + var w = new WebSpace() + { + CloudPanel = cloudPanel, + Owner = owner, + Domain = domain, + UserName = ftpLogin, + Password = ftpPassword, + VHostTemplate = "Generic" //TODO: Implement as select option + }; + + var webSpace = WebSpaceRepository.Add(w); + + try + { + await CloudPanelApiHelper.Post(cloudPanel, "site/php", new AddPhpSite() + { + VHostTemplate = w.VHostTemplate, + DomainName = w.Domain, + PhpVersion = phpVersion, + SiteUser = w.UserName, + SiteUserPassword = w.Password + }); + } + catch (Exception) + { + WebSpaceRepository.Delete(webSpace); + throw; + } + + return webSpace; + } + + public async Task Delete(WebSpace w) + { + var website = EnsureData(w); + + await CloudPanelApiHelper.Delete(website.CloudPanel, $"site/{website.Domain}", null); + + WebSpaceRepository.Delete(website); + } + + public async Task IsHostUp(CloudPanel cloudPanel) + { + try + { + //var res = await PleskApiHelper.Get(pleskServer, "server"); + + return true; + + //if (res != null) + // return true; + } + catch (Exception e) + { + // ignored + } + + return false; + } + + public async Task IsHostUp(WebSpace w) + { + var webSpace = EnsureData(w); + + return await IsHostUp(webSpace.CloudPanel); + } + + #region SSL + public async Task GetSslCertificates(WebSpace w) + { + var certs = new List(); + return certs.ToArray(); + } + + public async Task CreateSslCertificate(WebSpace w) + { + + } + + public async Task DeleteSslCertificate(WebSpace w, string name) + { + + } + + #endregion + + #region Databases + + public async Task GetDatabases(WebSpace w) + { + return Array.Empty(); + } + + public async Task CreateDatabase(WebSpace w, string name, string password) + { + + } + + public async Task DeleteDatabase(WebSpace w, Models.Plesk.Resources.Database database) + { + + } + + #endregion + + public Task CreateFileAccess(WebSpace w) + { + var webspace = EnsureData(w); + + return Task.FromResult( + new SftpFileAccess(webspace.CloudPanel.Host, webspace.UserName, webspace.Password, 22, true) + ); + } + + private WebSpace EnsureData(WebSpace webSpace) + { + if (webSpace.CloudPanel == null || webSpace.Owner == null) + return WebSpaceRepository + .Get() + .Include(x => x.CloudPanel) + .Include(x => x.Owner) + .First(x => x.Id == webSpace.Id); + + return webSpace; + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs deleted file mode 100644 index 11906034..00000000 --- a/Moonlight/App/Services/WebsiteService.cs +++ /dev/null @@ -1,383 +0,0 @@ -using Logging.Net; -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database.Entities; -using Moonlight.App.Exceptions; -using Moonlight.App.Helpers; -using Moonlight.App.Helpers.Files; -using Moonlight.App.Models.Plesk.Requests; -using Moonlight.App.Models.Plesk.Resources; -using Moonlight.App.Repositories; -using FileAccess = Moonlight.App.Helpers.Files.FileAccess; - -namespace Moonlight.App.Services; - -public class WebsiteService -{ - private readonly WebsiteRepository WebsiteRepository; - private readonly PleskServerRepository PleskServerRepository; - private readonly PleskApiHelper PleskApiHelper; - private readonly UserRepository UserRepository; - - public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository, UserRepository userRepository) - { - WebsiteRepository = websiteRepository; - PleskApiHelper = pleskApiHelper; - PleskServerRepository = pleskServerRepository; - UserRepository = userRepository; - } - - public async Task Create(string baseDomain, User owner, PleskServer? ps = null) - { - if (WebsiteRepository.Get().Any(x => x.BaseDomain == baseDomain)) - throw new DisplayException("A website with this domain does already exist"); - - var pleskServer = ps ?? PleskServerRepository.Get().First(); - - var ftpLogin = baseDomain; - var ftpPassword = StringHelper.GenerateString(16); - - var w = new Website() - { - PleskServer = pleskServer, - Owner = owner, - BaseDomain = baseDomain, - PleskId = 0, - FtpPassword = ftpPassword, - FtpLogin = ftpLogin - }; - - var website = WebsiteRepository.Add(w); - - try - { - var id = await GetAdminAccount(pleskServer); - - var result = await PleskApiHelper.Post(pleskServer, "domains", new CreateDomain() - { - Description = $"moonlight website {website.Id}", - Name = baseDomain, - HostingType = "virtual", - Plan = new() - { - Name = "Unlimited" - }, - HostingSettings = new() - { - FtpLogin = ftpLogin, - FtpPassword = ftpPassword - }, - OwnerClient = new() - { - Id = id - } - }); - - website.PleskId = result.Id; - - WebsiteRepository.Update(website); - } - catch (Exception e) - { - WebsiteRepository.Delete(website); - throw; - } - - return website; - } - - public async Task Delete(Website w) - { - var website = EnsureData(w); - - await PleskApiHelper.Delete(website.PleskServer, $"domains/{w.PleskId}", null); - - WebsiteRepository.Delete(website); - } - - public async Task IsHostUp(PleskServer pleskServer) - { - try - { - var res = await PleskApiHelper.Get(pleskServer, "server"); - - if (res != null) - return true; - } - catch (Exception e) - { - // ignored - } - - return false; - } - - public async Task IsHostUp(Website w) - { - var website = EnsureData(w); - - try - { - var res = await PleskApiHelper.Get(website.PleskServer, "server"); - - if (res != null) - return true; - } - catch (Exception) - { - // ignored - } - - return false; - } - - #region Get host - - public async Task GetHost(PleskServer pleskServer) - { - return (await PleskApiHelper.Get(pleskServer, "server")).Hostname; - } - - public async Task GetHost(Website w) - { - var website = EnsureData(w); - - return await GetHost(website.PleskServer); - } - - #endregion - - private async Task GetAdminAccount(PleskServer pleskServer) - { - var users = await PleskApiHelper.Get(pleskServer, "clients"); - - var user = users.FirstOrDefault(x => x.Type == "admin"); - - if (user == null) - throw new DisplayException("No admin account in plesk found"); - - return user.Id; - } - - #region SSL - public async Task GetSslCertificates(Website w) - { - var website = EnsureData(w); - var certs = new List(); - - var data = await ExecuteCli(website.PleskServer, "certificate", p => - { - p.Add("-l"); - p.Add("-domain"); - p.Add(w.BaseDomain); - }); - - string[] lines = data.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); - - foreach (string line in lines) - { - if (line.Contains("Lets Encrypt")) - { - string[] parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - - if(parts.Length > 6) - certs.Add($"{parts[4]} {parts[5]} {parts[6]}"); - } - else if (line.Contains("Listing of SSL/TLS certificates repository was successful")) - { - // This line indicates the end of the certificate listing, so we can stop parsing - break; - } - } - - return certs.ToArray(); - } - - public async Task CreateSslCertificate(Website w) - { - var website = EnsureData(w); - - await ExecuteCli(website.PleskServer, "extension", p => - { - p.Add("--exec"); - p.Add("letsencrypt"); - p.Add("cli.php"); - p.Add("-d"); - p.Add(website.BaseDomain); - p.Add("-m"); - p.Add(website.Owner.Email); - }); - } - - public async Task DeleteSslCertificate(Website w, string name) - { - var website = EnsureData(w); - - try - { - await ExecuteCli(website.PleskServer, "site", p => - { - p.Add("-u"); - p.Add(website.BaseDomain); - p.Add("-ssl"); - p.Add("false"); - }); - - try - { - await ExecuteCli(website.PleskServer, "certificate", p => - { - p.Add("--remove"); - p.Add(name); - p.Add("-domain"); - p.Add(website.BaseDomain); - }); - } - catch (Exception e) - { - Logger.Warn("Error removing ssl certificate"); - Logger.Warn(e); - - throw new DisplayException("An unknown error occured while removing ssl certificate"); - } - } - catch (DisplayException) - { - // Redirect all display exception to soft error handler - throw; - } - catch (Exception e) - { - Logger.Warn("Error disabling ssl certificate"); - Logger.Warn(e); - - throw new DisplayException("An unknown error occured while disabling ssl certificate"); - } - } - - #endregion - - #region Databases - - public async Task GetDatabases(Website w) - { - var website = EnsureData(w); - - var dbs = await PleskApiHelper.Get( - website.PleskServer, - $"databases?domain={w.BaseDomain}" - ); - - return dbs; - } - - public async Task CreateDatabase(Website w, string name, string password) - { - var website = EnsureData(w); - - var server = await GetDefaultDatabaseServer(website); - - if (server == null) - throw new DisplayException("No database server marked as default found"); - - var dbReq = new CreateDatabase() - { - Name = name, - Type = "mysql", - ParentDomain = new() - { - Name = website.BaseDomain - }, - ServerId = server.Id - }; - - var db = await PleskApiHelper.Post(website.PleskServer, "databases", dbReq); - - if (db == null) - throw new DisplayException("Unable to create database via api"); - - var dbUserReq = new CreateDatabaseUser() - { - DatabaseId = db.Id, - Login = name, - Password = password - }; - - await PleskApiHelper.Post(website.PleskServer, "dbusers", dbUserReq); - } - - public async Task DeleteDatabase(Website w, Models.Plesk.Resources.Database database) - { - var website = EnsureData(w); - - var dbUsers = await PleskApiHelper.Get( - website.PleskServer, - $"dbusers?dbId={database.Id}" - ); - - foreach (var dbUser in dbUsers) - { - await PleskApiHelper.Delete(website.PleskServer, $"dbusers/{dbUser.Id}", null); - } - - await PleskApiHelper.Delete(website.PleskServer, $"databases/{database.Id}", null); - } - - public async Task GetDefaultDatabaseServer(PleskServer pleskServer) - { - var dbServers = await PleskApiHelper.Get(pleskServer, "dbservers"); - - return dbServers.FirstOrDefault(x => x.IsDefault); - } - - public async Task GetDefaultDatabaseServer(Website w) - { - var website = EnsureData(w); - - return await GetDefaultDatabaseServer(website.PleskServer); - } - - #endregion - - public async Task CreateFileAccess(Website w) - { - var website = EnsureData(w); - var host = await GetHost(website.PleskServer); - - return new FtpFileAccess(host, 21, website.FtpLogin, website.FtpPassword); - } - - private async Task ExecuteCli( - PleskServer server, - string cli, Action>? parameters = null, - Action>? variables = null - ) - { - var p = new List(); - var v = new Dictionary(); - - parameters?.Invoke(p); - variables?.Invoke(v); - - var req = new CliCall() - { - Env = v, - Params = p - }; - - var res = await PleskApiHelper.Post(server, $"cli/{cli}/call", req); - - return res.Stdout; - } - - private Website EnsureData(Website website) - { - if (website.PleskServer == null || website.Owner == null) - return WebsiteRepository - .Get() - .Include(x => x.PleskServer) - .Include(x => x.Owner) - .First(x => x.Id == website.Id); - - return website; - } -} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 1c684c61..72e91ab4 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -41,6 +41,7 @@ + @@ -67,6 +68,7 @@ + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index ffd7894f..eba93a6f 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -2,6 +2,7 @@ using BlazorDownloadFile; using BlazorTable; using CurrieTechnologies.Razor.SweetAlert2; using Logging.Net; +using Moonlight.App.ApiClients.CloudPanel; using Moonlight.App.Database; using Moonlight.App.Helpers; using Moonlight.App.LogMigrator; @@ -76,6 +77,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(typeof(Repository<>)); // Services builder.Services.AddSingleton(); @@ -102,7 +104,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -136,6 +138,7 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); diff --git a/Moonlight/Shared/Components/FileManagerPartials/FilePath.razor b/Moonlight/Shared/Components/FileManagerPartials/FilePath.razor index 93982da1..4492bfcf 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FilePath.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FilePath.razor @@ -1,4 +1,5 @@ @using Moonlight.App.Helpers.Files +@using Logging.Net
diff --git a/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminWebspacesNavigation.razor similarity index 72% rename from Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor rename to Moonlight/Shared/Components/Navigations/AdminWebspacesNavigation.razor index 40b98026..b7380cc2 100644 --- a/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminWebspacesNavigation.razor @@ -2,13 +2,13 @@
diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor b/Moonlight/Shared/Components/WebsiteControl/WebSpaceDashboard.razor similarity index 92% rename from Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor rename to Moonlight/Shared/Components/WebsiteControl/WebSpaceDashboard.razor index d499e873..ab158cd8 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebSpaceDashboard.razor @@ -1,14 +1,14 @@ @using Moonlight.App.Database.Entities @using Moonlight.App.Services -@inject WebsiteService WebsiteService +@inject WebSpaceService WebSpaceService @inject SmartTranslateService SmartTranslateService
- Website screenshot + Website screenshot
@@ -85,7 +85,7 @@ @code { [CascadingParameter] - public Website CurrentWebsite { get; set; } + public WebSpace CurrentWebSpace { get; set; } private string[] Certs; @@ -94,18 +94,18 @@ private async Task Load(LazyLoader lazyLoader) { await lazyLoader.SetText("Loading certificates"); - Certs = await WebsiteService.GetSslCertificates(CurrentWebsite); + Certs = await WebSpaceService.GetSslCertificates(CurrentWebSpace); } private async Task CreateCertificate() { - await WebsiteService.CreateSslCertificate(CurrentWebsite); + await WebSpaceService.CreateSslCertificate(CurrentWebSpace); await LazyLoader.Reload(); } private async Task DeleteCertificate(string name) { - await WebsiteService.DeleteSslCertificate(CurrentWebsite, name); + await WebSpaceService.DeleteSslCertificate(CurrentWebSpace, name); await LazyLoader.Reload(); } } \ No newline at end of file diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor b/Moonlight/Shared/Components/WebsiteControl/WebSpaceDatabases.razor similarity index 90% rename from Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor rename to Moonlight/Shared/Components/WebsiteControl/WebSpaceDatabases.razor index 2c244069..5b83af5c 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebSpaceDatabases.razor @@ -4,7 +4,7 @@ @using Moonlight.App.Services @inject SmartTranslateService SmartTranslateService -@inject WebsiteService WebsiteService +@inject WebSpaceService WebSpaceService
@@ -93,7 +93,7 @@ else {
- No databases found for this website + No databases found for this webspace
} @@ -101,7 +101,7 @@ @code { [CascadingParameter] - public Website CurrentWebsite { get; set; } + public WebSpace CurrentWebSpace { get; set; } private LazyLoader LazyLoader; private Database[] Databases; @@ -112,25 +112,19 @@ private async Task Load(LazyLoader arg) { - Databases = await WebsiteService.GetDatabases(CurrentWebsite); - - if (Databases.Any()) - { - DatabaseServer = (await WebsiteService.GetDefaultDatabaseServer(CurrentWebsite))!; - Host = await WebsiteService.GetHost(CurrentWebsite); - } + Databases = await WebSpaceService.GetDatabases(CurrentWebSpace); } private async Task OnValidSubmit() { - await WebsiteService.CreateDatabase(CurrentWebsite, Model.Name, Model.Password); + await WebSpaceService.CreateDatabase(CurrentWebSpace, Model.Name, Model.Password); Model = new(); await LazyLoader.Reload(); } private async Task DeleteDatabase(Database database) { - await WebsiteService.DeleteDatabase(CurrentWebsite, database); + await WebSpaceService.DeleteDatabase(CurrentWebSpace, database); await LazyLoader.Reload(); } } \ No newline at end of file diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteFiles.razor b/Moonlight/Shared/Components/WebsiteControl/WebSpaceFiles.razor similarity index 69% rename from Moonlight/Shared/Components/WebsiteControl/WebsiteFiles.razor rename to Moonlight/Shared/Components/WebsiteControl/WebSpaceFiles.razor index 84d47508..06444027 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteFiles.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebSpaceFiles.razor @@ -3,7 +3,7 @@ @using Moonlight.App.Services @using Moonlight.Shared.Components.FileManagerPartials -@inject WebsiteService WebsiteService +@inject WebSpaceService WebSpaceService @@ -13,12 +13,12 @@ @code { [CascadingParameter] - public Website CurrentWebsite { get; set; } + public WebSpace CurrentWebSpace { get; set; } private FileAccess Access; private async Task Load(LazyLoader arg) { - Access = await WebsiteService.CreateFileAccess(CurrentWebsite); + Access = await WebSpaceService.CreateFileAccess(CurrentWebSpace); } } diff --git a/Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor b/Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor new file mode 100644 index 00000000..ffa1de43 --- /dev/null +++ b/Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor @@ -0,0 +1,55 @@ +@using Moonlight.App.Database.Entities +@using Moonlight.App.Services + +@inject WebSpaceService WebSpaceService + +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +@code +{ + [CascadingParameter] + public WebSpace CurrentWebSpace { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor b/Moonlight/Shared/Components/WebsiteControl/WebSpaceNavigation.razor similarity index 76% rename from Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor rename to Moonlight/Shared/Components/WebsiteControl/WebSpaceNavigation.razor index bec6068c..1d28f88c 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebSpaceNavigation.razor @@ -10,8 +10,8 @@
-
@(Website.BaseDomain)
-
@(Website.PleskServer.Name)
+
@(WebSpace.Domain)
+
@(WebSpace.CloudPanel.Name)
@@ -24,22 +24,22 @@
- +
diff --git a/Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor b/Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor deleted file mode 100644 index 7cb9ae2a..00000000 --- a/Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor +++ /dev/null @@ -1,98 +0,0 @@ -@page "/admin/websites/servers/edit/{Id:int}" - -@using Moonlight.App.Models.Forms -@using Moonlight.App.Repositories -@using Moonlight.App.Database.Entities - -@inject PleskServerRepository PleskServerRepository -@inject NavigationManager NavigationManager - - - - @if (PleskServer == null) - { -
-
- Not found image -
-

- Plesk server not found -

-

- A plesk server with that id cannot be found -

-
-
-
- } - else - { -
- - -
- -
- -
- -
- -
- -
-
- -
-
-
- } -
-
- -@code -{ - [Parameter] - public int Id { get; set; } - - private PleskServer? PleskServer; - - private PleskServerDataModel Model = new(); - - private Task OnValidSubmit() - { - PleskServer!.Name = Model.Name; - PleskServer.ApiUrl = Model.ApiUrl; - PleskServer.ApiKey = Model.ApiKey; - - PleskServerRepository.Update(PleskServer); - - NavigationManager.NavigateTo("/admin/websites/servers"); - - return Task.CompletedTask; - } - - private Task Load(LazyLoader arg) - { - PleskServer = PleskServerRepository - .Get() - .FirstOrDefault(x => x.Id == Id); - - if (PleskServer != null) - { - Model.Name = PleskServer.Name; - Model.ApiUrl = PleskServer.ApiUrl; - Model.ApiKey = PleskServer.ApiKey; - } - - return Task.CompletedTask; - } -} diff --git a/Moonlight/Shared/Views/Admin/Websites/Index.razor b/Moonlight/Shared/Views/Admin/Webspaces/Index.razor similarity index 53% rename from Moonlight/Shared/Views/Admin/Websites/Index.razor rename to Moonlight/Shared/Views/Admin/Webspaces/Index.razor index 6b233380..7518ca4a 100644 --- a/Moonlight/Shared/Views/Admin/Websites/Index.razor +++ b/Moonlight/Shared/Views/Admin/Webspaces/Index.razor @@ -1,4 +1,4 @@ -@page "/admin/websites/" +@page "/admin/webspaces/" @using Moonlight.Shared.Components.Navigations @using Moonlight.App.Services @@ -8,53 +8,53 @@ @using BlazorTable @inject SmartTranslateService SmartTranslateService -@inject WebsiteRepository WebsiteRepository -@inject WebsiteService WebsiteService +@inject Repository WebSpaceRepository +@inject WebSpaceService WebSpaceService - +