From ca64184faf3334308fc21fd0a87dcd85ad98d6a3 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 29 Apr 2023 23:37:03 +0200 Subject: [PATCH] Revert "Merge pull request #106 from Moonlight-Panel/DiscordBot" This reverts commit f71fcc0f5d27df5a7c93e3af235af4fe3d8c67b1, reversing changes made to e0bea9b61c79f3307cdcb14b888dd7604f871162. --- .../CloudPanel/CloudPanelApiHelper.cs | 83 ++ .../CloudPanel/CloudPanelException.cs | 32 + .../CloudPanel/Requests/AddDatabase.cs | 18 + .../CloudPanel/Requests/AddPhpSite.cs | 16 + .../CloudPanel/Requests/InstallLetsEncrypt.cs | 9 + Moonlight/App/Database/DataContext.cs | 14 +- .../{PleskServer.cs => CloudPanel.cs} | 3 +- .../App/Database/Entities/DockerImage.cs | 5 +- Moonlight/App/Database/Entities/Image.cs | 5 +- .../App/Database/Entities/ImageVariable.cs | 5 +- .../App/Database/Entities/MySqlDatabase.cs | 9 + .../Database/Entities/SupportChatMessage.cs | 21 + .../App/Database/Entities/SupportMessage.cs | 17 - Moonlight/App/Database/Entities/User.cs | 5 +- Moonlight/App/Database/Entities/WebSpace.cs | 13 + Moonlight/App/Database/Entities/Website.cs | 12 - .../Interceptors/SqlLoggingInterceptor.cs | 40 + ...19120719_AddedCloudPanelModels.Designer.cs | 1099 ++++++++++++++++ .../20230419120719_AddedCloudPanelModels.cs | 121 ++ ...ddedHostFieldToCloudPanelModel.Designer.cs | 1103 +++++++++++++++++ ...9125155_AddedHostFieldToCloudPanelModel.cs | 29 + ...3846_AddedNewSupportChatModels.Designer.cs | 1087 ++++++++++++++++ ...0230420213846_AddedNewSupportChatModels.cs | 138 +++ ...556_RemovedOldSupportChatModel.Designer.cs | 1028 +++++++++++++++ ...230421143556_RemovedOldSupportChatModel.cs | 67 + .../Migrations/DataContextModelSnapshot.cs | 174 ++- Moonlight/App/Events/EventSystem.cs | 142 +++ Moonlight/App/Events/Subscriber.cs | 8 + Moonlight/App/Exceptions/DaemonException.cs | 2 +- Moonlight/App/Exceptions/PleskException.cs | 2 +- Moonlight/App/Exceptions/WingsException.cs | 2 +- Moonlight/App/Extensions/DbSetExtensions.cs | 14 + Moonlight/App/Helpers/Files/SftpFileAccess.cs | 206 +++ Moonlight/App/Helpers/ParseHelper.cs | 71 +- Moonlight/App/Helpers/PleskApiHelper.cs | 220 ---- Moonlight/App/Helpers/SyncStreamAdapter.cs | 58 + .../Api/Moonlight/OAuth2Controller.cs | 8 +- .../Api/Remote/BackupController.cs | 13 +- .../Controllers/Api/Remote/DdosController.cs | 9 +- .../Api/Remote/ServersController.cs | 19 +- Moonlight/App/MessageSystem/MessageSender.cs | 83 -- .../App/MessageSystem/MessageSubscriber.cs | 9 - .../App/Models/Forms/CloudPanelDataModel.cs | 19 + .../App/Models/Forms/DomainOrderDataModel.cs | 2 +- .../App/Models/Forms/ServerEditDataModel.cs | 29 + .../Repositories/NodeAllocationRepository.cs | 28 + .../App/Repositories/PleskServerRepository.cs | 44 - Moonlight/App/Repositories/Repository.cs | 40 + .../App/Repositories/StatisticsRepository.cs | 7 +- .../Repositories/SupportMessageRepository.cs | 44 - .../App/Repositories/WebsiteRepository.cs | 44 - Moonlight/App/Services/CleanupService.cs | 20 +- Moonlight/App/Services/ConfigService.cs | 9 + Moonlight/App/Services/DateTimeService.cs | 31 + .../Services/DiscordNotificationService.cs | 99 ++ Moonlight/App/Services/FabricService.cs | 96 ++ Moonlight/App/Services/FileDownloadService.cs | 33 + Moonlight/App/Services/ForgeService.cs | 37 + .../App/Services/Interop/ClipboardService.cs | 9 +- .../App/Services/Interop/ToastService.cs | 14 +- Moonlight/App/Services/MailService.cs | 34 +- Moonlight/App/Services/MessageService.cs | 11 - Moonlight/App/Services/ServerService.cs | 72 +- .../App/Services/Sessions/IdentityService.cs | 7 +- .../App/Services/Sessions/SessionService.cs | 11 +- Moonlight/App/Services/SmartDeployService.cs | 24 +- .../Statistics/StatisticsCaptureService.cs | 81 +- .../Statistics/StatisticsViewService.cs | 6 +- Moonlight/App/Services/SubscriptionService.cs | 31 +- .../Services/Support/SupportAdminService.cs | 132 -- .../Services/Support/SupportClientService.cs | 124 -- .../Services/Support/SupportServerService.cs | 138 --- .../SupportChat/SupportChatAdminService.cs | 134 ++ .../SupportChat/SupportChatClientService.cs | 128 ++ .../SupportChat/SupportChatServerService.cs | 146 +++ Moonlight/App/Services/UserService.cs | 18 +- Moonlight/App/Services/WebSpaceService.cs | 191 +++ Moonlight/App/Services/WebsiteService.cs | 383 ------ Moonlight/Moonlight.csproj | 4 + Moonlight/Pages/_Host.cshtml | 53 +- Moonlight/Pages/_Layout.cshtml | 28 +- Moonlight/Program.cs | 47 +- Moonlight/Properties/launchSettings.json | 11 +- .../ErrorBoundaries/SoftErrorBoundary.razor | 11 +- .../FileManagerPartials/FileEditor.razor | 2 +- .../FileManagerPartials/FilePath.razor | 1 + .../FileManagerPartials/FileView.razor | 157 +-- ...n.razor => AdminWebspacesNavigation.razor} | 8 +- .../Components/Partials/ContentBlock.razor | 52 + .../Shared/Components/Partials/Navbar.razor | 53 +- .../Components/Partials/SidebarMenu.razor | 8 +- .../Components/Partials/ThemeSwitcher.razor | 2 +- .../ServerControl/ServerBackups.razor | 157 ++- .../ServerControl/ServerNavigation.razor | 14 +- .../ServerControl/ServerSettings.razor | 6 + .../Settings/FabricVersionSetting.razor | 173 +++ .../Settings/ForgeVersionSetting.razor | 147 +++ .../WebsiteControl/WebSpaceDashboard.razor | 60 + ...atabases.razor => WebSpaceDatabases.razor} | 46 +- ...WebsiteFiles.razor => WebSpaceFiles.razor} | 6 +- ...igation.razor => WebSpaceNavigation.razor} | 16 +- .../WebsiteControl/WebSpaceSftp.razor | 55 + .../WebsiteControl/WebsiteDashboard.razor | 111 -- .../WebsiteControl/WebsiteFtp.razor | 64 - .../Shared/Components/Xterm/Terminal.razor | 4 +- Moonlight/Shared/Layouts/MainLayout.razor | 123 +- .../Shared/Views/Admin/Domains/Index.razor | 1 + Moonlight/Shared/Views/Admin/Index.razor | 19 +- Moonlight/Shared/Views/Admin/Nodes/Ddos.razor | 16 +- Moonlight/Shared/Views/Admin/Nodes/Edit.razor | 42 + .../Shared/Views/Admin/Servers/Cleanup.razor | 18 +- .../Shared/Views/Admin/Servers/Edit.razor | 292 +++-- .../Views/Admin/Servers/Images/Edit.razor | 15 + .../Views/Admin/Servers/Images/Index.razor | 93 +- Moonlight/Shared/Views/Admin/Statistics.razor | 154 ++- .../Shared/Views/Admin/Support/Index.razor | 81 +- .../Shared/Views/Admin/Support/View.razor | 94 +- .../Shared/Views/Admin/Sys/Resources.razor | 32 +- Moonlight/Shared/Views/Admin/Users/Edit.razor | 45 +- .../Shared/Views/Admin/Users/Sessions.razor | 2 +- .../Views/Admin/Websites/Servers/Edit.razor | 98 -- .../Admin/{Websites => Webspaces}/Index.razor | 44 +- .../Admin/{Websites => Webspaces}/New.razor | 8 +- .../Views/Admin/Webspaces/Servers/Edit.razor | 102 ++ .../Servers/Index.razor | 49 +- .../{Websites => Webspaces}/Servers/New.razor | 25 +- Moonlight/Shared/Views/Changelog.razor | 211 +++- Moonlight/Shared/Views/Index.razor | 22 +- Moonlight/Shared/Views/Profile/Index.razor | 50 +- Moonlight/Shared/Views/Server/Index.razor | 11 +- Moonlight/Shared/Views/Servers/Create.razor | 6 +- Moonlight/Shared/Views/Servers/Index.razor | 1 + Moonlight/Shared/Views/Support.razor | 107 +- .../Views/{Website => Webspace}/Index.razor | 49 +- .../{Websites => Webspaces}/Create.razor | 44 +- .../Views/{Websites => Webspaces}/Index.razor | 24 +- Moonlight/defaultstorage/configs/config.json | 11 +- .../configs/default_subscription.json | 1 + 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 | 360 +++++- 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 - .../wwwroot/assets/media/svg/loading.svg | 1 + 152 files changed, 9159 insertions(+), 3251 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/AddDatabase.cs create mode 100644 Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs create mode 100644 Moonlight/App/ApiClients/CloudPanel/Requests/InstallLetsEncrypt.cs rename Moonlight/App/Database/Entities/{PleskServer.cs => CloudPanel.cs} (76%) create mode 100644 Moonlight/App/Database/Entities/MySqlDatabase.cs create mode 100644 Moonlight/App/Database/Entities/SupportChatMessage.cs delete mode 100644 Moonlight/App/Database/Entities/SupportMessage.cs create mode 100644 Moonlight/App/Database/Entities/WebSpace.cs delete mode 100644 Moonlight/App/Database/Entities/Website.cs create mode 100644 Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.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/Database/Migrations/20230420213846_AddedNewSupportChatModels.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230420213846_AddedNewSupportChatModels.cs create mode 100644 Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.cs create mode 100644 Moonlight/App/Events/EventSystem.cs create mode 100644 Moonlight/App/Events/Subscriber.cs create mode 100644 Moonlight/App/Extensions/DbSetExtensions.cs create mode 100644 Moonlight/App/Helpers/Files/SftpFileAccess.cs delete mode 100644 Moonlight/App/Helpers/PleskApiHelper.cs create mode 100644 Moonlight/App/Helpers/SyncStreamAdapter.cs delete mode 100644 Moonlight/App/MessageSystem/MessageSender.cs delete mode 100644 Moonlight/App/MessageSystem/MessageSubscriber.cs create mode 100644 Moonlight/App/Models/Forms/CloudPanelDataModel.cs create mode 100644 Moonlight/App/Models/Forms/ServerEditDataModel.cs create mode 100644 Moonlight/App/Repositories/NodeAllocationRepository.cs delete mode 100644 Moonlight/App/Repositories/PleskServerRepository.cs create mode 100644 Moonlight/App/Repositories/Repository.cs delete mode 100644 Moonlight/App/Repositories/SupportMessageRepository.cs delete mode 100644 Moonlight/App/Repositories/WebsiteRepository.cs create mode 100644 Moonlight/App/Services/DateTimeService.cs create mode 100644 Moonlight/App/Services/DiscordNotificationService.cs create mode 100644 Moonlight/App/Services/FabricService.cs create mode 100644 Moonlight/App/Services/FileDownloadService.cs create mode 100644 Moonlight/App/Services/ForgeService.cs delete mode 100644 Moonlight/App/Services/MessageService.cs delete mode 100644 Moonlight/App/Services/Support/SupportAdminService.cs delete mode 100644 Moonlight/App/Services/Support/SupportClientService.cs delete mode 100644 Moonlight/App/Services/Support/SupportServerService.cs create mode 100644 Moonlight/App/Services/SupportChat/SupportChatAdminService.cs create mode 100644 Moonlight/App/Services/SupportChat/SupportChatClientService.cs create mode 100644 Moonlight/App/Services/SupportChat/SupportChatServerService.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%) create mode 100644 Moonlight/Shared/Components/Partials/ContentBlock.razor create mode 100644 Moonlight/Shared/Components/ServerControl/Settings/FabricVersionSetting.razor create mode 100644 Moonlight/Shared/Components/ServerControl/Settings/ForgeVersionSetting.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebSpaceDashboard.razor rename Moonlight/Shared/Components/WebsiteControl/{WebsiteDatabases.razor => WebSpaceDatabases.razor} (77%) rename Moonlight/Shared/Components/WebsiteControl/{WebsiteFiles.razor => WebSpaceFiles.razor} (69%) rename Moonlight/Shared/Components/WebsiteControl/{WebsiteNavigation.razor => WebSpaceNavigation.razor} (75%) create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebSpaceSftp.razor delete mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor 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 (54%) 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 (71%) rename Moonlight/Shared/Views/{Websites => Webspaces}/Create.razor (77%) rename Moonlight/Shared/Views/{Websites => Webspaces}/Index.razor (75%) create mode 100644 Moonlight/defaultstorage/configs/default_subscription.json 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 create mode 100644 Moonlight/wwwroot/assets/media/svg/loading.svg 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/AddDatabase.cs b/Moonlight/App/ApiClients/CloudPanel/Requests/AddDatabase.cs new file mode 100644 index 00000000..26d8d4bb --- /dev/null +++ b/Moonlight/App/ApiClients/CloudPanel/Requests/AddDatabase.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.ApiClients.CloudPanel.Requests; + +public class AddDatabase +{ + [JsonProperty("domainName")] + public string DomainName { get; set; } + + [JsonProperty("databaseName")] + public string DatabaseName { get; set; } + + [JsonProperty("databaseUserName")] + public string DatabaseUserName { get; set; } + + [JsonProperty("databaseUserPassword")] + public string DatabaseUserPassword { get; set; } +} \ 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/ApiClients/CloudPanel/Requests/InstallLetsEncrypt.cs b/Moonlight/App/ApiClients/CloudPanel/Requests/InstallLetsEncrypt.cs new file mode 100644 index 00000000..e10068fd --- /dev/null +++ b/Moonlight/App/ApiClients/CloudPanel/Requests/InstallLetsEncrypt.cs @@ -0,0 +1,9 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.ApiClients.CloudPanel.Requests; + +public class InstallLetsEncrypt +{ + [JsonProperty("domainName")] + public string DomainName { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index dc3e77de..9c92b5b8 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; @@ -29,8 +30,7 @@ public class DataContext : DbContext public DbSet AuditLog { get; set; } public DbSet ErrorLog { get; set; } public DbSet SecurityLog { get; set; } - public DbSet SupportMessages { get; set; } - + public DbSet SharedDomains { get; set; } public DbSet Domains { get; set; } public DbSet Revokes { get; set; } @@ -38,10 +38,13 @@ public class DataContext : DbContext public DbSet NotificationActions { get; set; } public DbSet DdosAttacks { get; set; } public DbSet Subscriptions { get; set; } - public DbSet PleskServers { get; set; } - 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; } + public DbSet SupportChatMessages { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -65,6 +68,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/Entities/PleskServer.cs b/Moonlight/App/Database/Entities/CloudPanel.cs similarity index 76% rename from Moonlight/App/Database/Entities/PleskServer.cs rename to Moonlight/App/Database/Entities/CloudPanel.cs index a4cd6307..de872b0f 100644 --- a/Moonlight/App/Database/Entities/PleskServer.cs +++ b/Moonlight/App/Database/Entities/CloudPanel.cs @@ -1,9 +1,10 @@ namespace Moonlight.App.Database.Entities; -public class PleskServer +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/DockerImage.cs b/Moonlight/App/Database/Entities/DockerImage.cs index 4ad61376..047d1faa 100644 --- a/Moonlight/App/Database/Entities/DockerImage.cs +++ b/Moonlight/App/Database/Entities/DockerImage.cs @@ -1,7 +1,10 @@ -namespace Moonlight.App.Database.Entities; +using Newtonsoft.Json; + +namespace Moonlight.App.Database.Entities; public class DockerImage { + [JsonIgnore] public int Id { get; set; } public bool Default { get; set; } = false; public string Name { get; set; } = ""; diff --git a/Moonlight/App/Database/Entities/Image.cs b/Moonlight/App/Database/Entities/Image.cs index 6e2b6041..1711940d 100644 --- a/Moonlight/App/Database/Entities/Image.cs +++ b/Moonlight/App/Database/Entities/Image.cs @@ -1,7 +1,10 @@ -namespace Moonlight.App.Database.Entities; +using Newtonsoft.Json; + +namespace Moonlight.App.Database.Entities; public class Image { + [JsonIgnore] public int Id { get; set; } public Guid Uuid { get; set; } public string Name { get; set; } = ""; diff --git a/Moonlight/App/Database/Entities/ImageVariable.cs b/Moonlight/App/Database/Entities/ImageVariable.cs index dd14b223..8a394a70 100644 --- a/Moonlight/App/Database/Entities/ImageVariable.cs +++ b/Moonlight/App/Database/Entities/ImageVariable.cs @@ -1,7 +1,10 @@ -namespace Moonlight.App.Database.Entities; +using Newtonsoft.Json; + +namespace Moonlight.App.Database.Entities; public class ImageVariable { + [JsonIgnore] public int Id { get; set; } public string Key { get; set; } = ""; public string DefaultValue { get; set; } = ""; 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/SupportChatMessage.cs b/Moonlight/App/Database/Entities/SupportChatMessage.cs new file mode 100644 index 00000000..47ad3e6b --- /dev/null +++ b/Moonlight/App/Database/Entities/SupportChatMessage.cs @@ -0,0 +1,21 @@ +using Moonlight.App.Models.Misc; + +namespace Moonlight.App.Database.Entities; + +public class SupportChatMessage +{ + public int Id { get; set; } + + public string Content { get; set; } = ""; + public string Attachment { get; set; } = ""; + + public User? Sender { get; set; } + public User Recipient { get; set; } + + public bool IsQuestion { get; set; } = false; + public QuestionType QuestionType { get; set; } + public string Answer { get; set; } = ""; + + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/SupportMessage.cs b/Moonlight/App/Database/Entities/SupportMessage.cs deleted file mode 100644 index df1019d4..00000000 --- a/Moonlight/App/Database/Entities/SupportMessage.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Moonlight.App.Models.Misc; - -namespace Moonlight.App.Database.Entities; - -public class SupportMessage -{ - public int Id { get; set; } - public string Message { get; set; } = ""; - public User? Sender { get; set; } = null; - public User? Recipient { get; set; } = null; - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public bool IsQuestion { get; set; } = false; - public QuestionType Type { get; set; } - public string Answer { get; set; } = ""; - public bool IsSystem { get; set; } = false; - public bool IsSupport { get; set; } = false; -} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/User.cs b/Moonlight/App/Database/Entities/User.cs index 790d4e20..254ca5d6 100644 --- a/Moonlight/App/Database/Entities/User.cs +++ b/Moonlight/App/Database/Entities/User.cs @@ -1,5 +1,4 @@ -using System.ComponentModel.DataAnnotations; -using Moonlight.App.Models.Misc; +using Moonlight.App.Models.Misc; namespace Moonlight.App.Database.Entities; @@ -34,7 +33,7 @@ public class User // Security public bool TotpEnabled { get; set; } = false; public string TotpSecret { get; set; } = ""; - public DateTime TokenValidTime { get; set; } = DateTime.Now; + public DateTime TokenValidTime { get; set; } = DateTime.UtcNow; // Discord public ulong DiscordId { get; set; } 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/Entities/Website.cs b/Moonlight/App/Database/Entities/Website.cs deleted file mode 100644 index 56e9fd12..00000000 --- a/Moonlight/App/Database/Entities/Website.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Moonlight.App.Database.Entities; - -public class Website -{ - public int Id { get; set; } - public string BaseDomain { get; set; } = ""; - public int PleskId { get; set; } - public PleskServer PleskServer { get; set; } - public User Owner { get; set; } - public string FtpLogin { get; set; } = ""; - public string FtpPassword { get; set; } = ""; -} \ 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/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/20230420213846_AddedNewSupportChatModels.Designer.cs b/Moonlight/App/Database/Migrations/20230420213846_AddedNewSupportChatModels.Designer.cs new file mode 100644 index 00000000..7cea0436 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230420213846_AddedNewSupportChatModels.Designer.cs @@ -0,0 +1,1087 @@ +// +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("20230420213846_AddedNewSupportChatModels")] + partial class AddedNewSupportChatModels + { + /// + 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.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.SupportChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Attachment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportChatMessages"); + }); + + 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.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.SupportChatMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + 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.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/20230420213846_AddedNewSupportChatModels.cs b/Moonlight/App/Database/Migrations/20230420213846_AddedNewSupportChatModels.cs new file mode 100644 index 00000000..e8160af4 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230420213846_AddedNewSupportChatModels.cs @@ -0,0 +1,138 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedNewSupportChatModels : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Websites"); + + migrationBuilder.DropTable( + name: "PleskServers"); + + migrationBuilder.CreateTable( + name: "SupportChatMessages", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Content = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Attachment = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + SenderId = table.Column(type: "int", nullable: true), + RecipientId = table.Column(type: "int", nullable: false), + IsQuestion = table.Column(type: "tinyint(1)", nullable: false), + QuestionType = table.Column(type: "int", nullable: false), + Answer = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupportChatMessages", x => x.Id); + table.ForeignKey( + name: "FK_SupportChatMessages_Users_RecipientId", + column: x => x.RecipientId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SupportChatMessages_Users_SenderId", + column: x => x.SenderId, + principalTable: "Users", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_SupportChatMessages_RecipientId", + table: "SupportChatMessages", + column: "RecipientId"); + + migrationBuilder.CreateIndex( + name: "IX_SupportChatMessages_SenderId", + table: "SupportChatMessages", + column: "SenderId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SupportChatMessages"); + + migrationBuilder.CreateTable( + name: "PleskServers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ApiKey = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiUrl = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_PleskServers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Websites", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + OwnerId = table.Column(type: "int", nullable: false), + PleskServerId = table.Column(type: "int", nullable: false), + BaseDomain = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FtpLogin = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FtpPassword = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PleskId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Websites", x => x.Id); + table.ForeignKey( + name: "FK_Websites_PleskServers_PleskServerId", + column: x => x.PleskServerId, + principalTable: "PleskServers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Websites_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_OwnerId", + table: "Websites", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_PleskServerId", + table: "Websites", + column: "PleskServerId"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.Designer.cs b/Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.Designer.cs new file mode 100644 index 00000000..d34006e9 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.Designer.cs @@ -0,0 +1,1028 @@ +// +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("20230421143556_RemovedOldSupportChatModel")] + partial class RemovedOldSupportChatModel + { + /// + 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.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.SupportChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Attachment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportChatMessages"); + }); + + 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.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.SupportChatMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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/20230421143556_RemovedOldSupportChatModel.cs b/Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.cs new file mode 100644 index 00000000..c89742ee --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230421143556_RemovedOldSupportChatModel.cs @@ -0,0 +1,67 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class RemovedOldSupportChatModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SupportMessages"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SupportMessages", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RecipientId = table.Column(type: "int", nullable: true), + SenderId = table.Column(type: "int", nullable: true), + Answer = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsQuestion = table.Column(type: "tinyint(1)", nullable: false), + IsSupport = table.Column(type: "tinyint(1)", nullable: false), + IsSystem = table.Column(type: "tinyint(1)", nullable: false), + Message = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupportMessages", x => x.Id); + table.ForeignKey( + name: "FK_SupportMessages_Users_RecipientId", + column: x => x.RecipientId, + principalTable: "Users", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_SupportMessages_Users_SenderId", + column: x => x.SenderId, + principalTable: "Users", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_SupportMessages_RecipientId", + table: "SupportMessages", + column: "RecipientId"); + + migrationBuilder.CreateIndex( + name: "IX_SupportMessages_SenderId", + table: "SupportMessages", + column: "SenderId"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 7a0da1a7..f6f8d75a 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") @@ -402,29 +453,6 @@ namespace Moonlight.App.Database.Migrations 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") @@ -622,7 +650,7 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Subscriptions"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -632,30 +660,31 @@ namespace Moonlight.App.Database.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("Attachment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + b.Property("CreatedAt") .HasColumnType("datetime(6)"); b.Property("IsQuestion") .HasColumnType("tinyint(1)"); - b.Property("IsSupport") - .HasColumnType("tinyint(1)"); + b.Property("QuestionType") + .HasColumnType("int"); - b.Property("IsSystem") - .HasColumnType("tinyint(1)"); - - b.Property("Message") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("RecipientId") + b.Property("RecipientId") .HasColumnType("int"); b.Property("SenderId") .HasColumnType("int"); - b.Property("Type") - .HasColumnType("int"); + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -663,7 +692,7 @@ namespace Moonlight.App.Database.Migrations b.HasIndex("SenderId"); - b.ToTable("SupportMessages"); + b.ToTable("SupportChatMessages"); }); modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => @@ -748,40 +777,41 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("BaseDomain") - .IsRequired() - .HasColumnType("longtext"); + b.Property("CloudPanelId") + .HasColumnType("int"); - b.Property("FtpLogin") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("FtpPassword") + b.Property("Domain") .IsRequired() .HasColumnType("longtext"); b.Property("OwnerId") .HasColumnType("int"); - b.Property("PleskId") - .HasColumnType("int"); + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); - b.Property("PleskServerId") - .HasColumnType("int"); + b.Property("UserName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VHostTemplate") + .IsRequired() + .HasColumnType("longtext"); b.HasKey("Id"); + b.HasIndex("CloudPanelId"); + b.HasIndex("OwnerId"); - b.HasIndex("PleskServerId"); - - b.ToTable("Websites"); + b.ToTable("WebSpaces"); }); modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => @@ -828,6 +858,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) @@ -908,11 +949,13 @@ namespace Moonlight.App.Database.Migrations .HasForeignKey("ServerId"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b => { b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") .WithMany() - .HasForeignKey("RecipientId"); + .HasForeignKey("RecipientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.HasOne("Moonlight.App.Database.Entities.User", "Sender") .WithMany() @@ -932,23 +975,23 @@ namespace Moonlight.App.Database.Migrations b.Navigation("CurrentSubscription"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + 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.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") - .WithMany() - .HasForeignKey("PleskServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + b.Navigation("CloudPanel"); b.Navigation("Owner"); - - b.Navigation("PleskServer"); }); modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => @@ -971,6 +1014,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/Events/EventSystem.cs b/Moonlight/App/Events/EventSystem.cs new file mode 100644 index 00000000..1d0f1d40 --- /dev/null +++ b/Moonlight/App/Events/EventSystem.cs @@ -0,0 +1,142 @@ +using System.Diagnostics; +using Logging.Net; + +namespace Moonlight.App.Events; + +public class EventSystem +{ + private Dictionary Storage = new(); + private List Subscribers = new(); + + private readonly bool Debug = false; + private readonly bool DisableWarning = false; + private readonly TimeSpan TookToLongTime = TimeSpan.FromSeconds(1); + + public Task On(string id, object handle, Func action) + { + if(Debug) + Logger.Debug($"{handle} subscribed to '{id}'"); + + lock (Subscribers) + { + if (!Subscribers.Any(x => x.Id == id && x.Handle == handle)) + { + Subscribers.Add(new () + { + Action = action, + Handle = handle, + Id = id + }); + } + } + + return Task.CompletedTask; + } + + public Task Emit(string id, object? d = null) + { + int hashCode = -1; + + if (d != null) + { + hashCode = d.GetHashCode(); + Storage.TryAdd(hashCode, d); + } + + Subscriber[] subscribers; + + lock (Subscribers) + { + subscribers = Subscribers + .Where(x => x.Id == id) + .ToArray(); + } + + var tasks = new List(); + + foreach (var subscriber in subscribers) + { + tasks.Add(new Task(() => + { + int storageId = hashCode + 0; // To create a copy of the hash code + + object? data = null; + + if (storageId != -1) + { + if (Storage.TryGetValue(storageId, out var value)) + { + data = value; + } + else + { + Logger.Warn($"Object with the hash '{storageId}' was not present in the storage"); + return; + } + } + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + + var del = (Delegate)subscriber.Action; + + try + { + ((Task)del.DynamicInvoke(data)!).Wait(); + } + catch (Exception e) + { + Logger.Warn($"Error emitting '{subscriber.Id} on {subscriber.Handle}'"); + Logger.Warn(e); + } + + stopWatch.Stop(); + + if (!DisableWarning) + { + if (stopWatch.Elapsed.TotalMilliseconds > TookToLongTime.TotalMilliseconds) + { + Logger.Warn($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took long to process. {stopWatch.Elapsed.TotalMilliseconds}ms"); + } + } + + if (Debug) + { + Logger.Debug($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took {stopWatch.Elapsed.TotalMilliseconds}ms"); + } + })); + } + + foreach (var task in tasks) + { + task.Start(); + } + + Task.Run(() => + { + Task.WaitAll(tasks.ToArray()); + Storage.Remove(hashCode); + + if(Debug) + Logger.Debug($"Completed all event tasks for '{id}' and removed object from storage"); + }); + + if(Debug) + Logger.Debug($"Completed event emit '{id}'"); + + return Task.CompletedTask; + } + + public Task Off(string id, object handle) + { + if(Debug) + Logger.Debug($"{handle} unsubscribed to '{id}'"); + + lock (Subscribers) + { + Subscribers.RemoveAll(x => x.Id == id && x.Handle == handle); + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight/App/Events/Subscriber.cs b/Moonlight/App/Events/Subscriber.cs new file mode 100644 index 00000000..0d64012c --- /dev/null +++ b/Moonlight/App/Events/Subscriber.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Events; + +public class Subscriber +{ + public string Id { get; set; } + public object Action { get; set; } + public object Handle { get; set; } +} \ No newline at end of file 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/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/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/ParseHelper.cs b/Moonlight/App/Helpers/ParseHelper.cs index ecc983dd..b3074c1c 100644 --- a/Moonlight/App/Helpers/ParseHelper.cs +++ b/Moonlight/App/Helpers/ParseHelper.cs @@ -1,15 +1,23 @@ -namespace Moonlight.App.Helpers; +using Logging.Net; + +namespace Moonlight.App.Helpers; public static class ParseHelper { public static int MinecraftToInt(string raw) { - var versionWithoutPre = raw.Split("-")[0]; + var versionWithoutPre = raw.Split("_")[0]; + versionWithoutPre = versionWithoutPre.Split("-")[0]; + + // Fuck you 1.7.10 ;) + versionWithoutPre = versionWithoutPre.Replace("1.7.10", "1.7"); if (versionWithoutPre.Count(x => x == "."[0]) == 1) versionWithoutPre += ".0"; - return int.Parse(versionWithoutPre.Replace(".", "")); + var x = versionWithoutPre.Replace(".", ""); + + return int.Parse(x); } public static string FirstPartStartingWithNumber(string raw) @@ -29,4 +37,61 @@ public static class ParseHelper return res; } + + public static string GetHighestVersion(string[] versions) + { + // Initialize the highest version to the first version in the array + string highestVersion = versions[0]; + + // Loop through the remaining versions in the array + for (int i = 1; i < versions.Length; i++) + { + // Compare the current version to the highest version + if (CompareVersions(versions[i], highestVersion) > 0) + { + // If the current version is higher, update the highest version + highestVersion = versions[i]; + } + } + + return highestVersion; + } + + public static int CompareVersions(string version1, string version2) + { + // Split the versions into their component parts + string[] version1Parts = version1.Split('.'); + string[] version2Parts = version2.Split('.'); + + // Compare each component part in turn + for (int i = 0; i < version1Parts.Length && i < version2Parts.Length; i++) + { + int part1 = int.Parse(version1Parts[i]); + int part2 = int.Parse(version2Parts[i]); + + if (part1 < part2) + { + return -1; + } + else if (part1 > part2) + { + return 1; + } + } + + // If we get here, the versions are equal up to the length of the shorter one. + // If one version has more parts than the other, the longer one is considered higher. + if (version1Parts.Length < version2Parts.Length) + { + return -1; + } + else if (version1Parts.Length > version2Parts.Length) + { + return 1; + } + else + { + return 0; + } + } } \ No newline at end of file diff --git a/Moonlight/App/Helpers/PleskApiHelper.cs b/Moonlight/App/Helpers/PleskApiHelper.cs deleted file mode 100644 index 87ea9878..00000000 --- a/Moonlight/App/Helpers/PleskApiHelper.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System.Text; -using Moonlight.App.Database.Entities; -using Moonlight.App.Exceptions; -using Newtonsoft.Json; -using RestSharp; - -namespace Moonlight.App.Helpers; - -public class PleskApiHelper -{ - private readonly RestClient Client; - - public PleskApiHelper() - { - Client = new(); - } - - public async Task Get(PleskServer server, string resource) - { - var request = CreateRequest(server, resource); - - request.Method = Method.Get; - - var response = await Client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - if (response.StatusCode != 0) - { - throw new PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - - return JsonConvert.DeserializeObject(response.Content!)!; - } - - public async Task GetRaw(PleskServer server, string resource) - { - var request = CreateRequest(server, resource); - - request.Method = Method.Get; - - var response = await Client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - if (response.StatusCode != 0) - { - throw new PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - - return response.Content!; - } - - public async Task Post(PleskServer server, string resource, object? body) - { - var request = CreateRequest(server, resource); - - request.Method = Method.Post; - - request.AddParameter("text/plain", - JsonConvert.SerializeObject(body), - ParameterType.RequestBody - ); - - var response = await Client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - if (response.StatusCode != 0) - { - throw new PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - - return JsonConvert.DeserializeObject(response.Content!)!; - } - - public async Task Post(PleskServer server, string resource, object? body) - { - var request = CreateRequest(server, 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 PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - } - - public async Task PostRaw(PleskServer server, string resource, object body) - { - var request = CreateRequest(server, resource); - - request.Method = Method.Post; - - request.AddParameter("text/plain", body, ParameterType.RequestBody); - - var response = await Client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - if (response.StatusCode != 0) - { - throw new PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - } - - public async Task Delete(PleskServer server, string resource, object? body) - { - var request = CreateRequest(server, 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 PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - } - - public async Task Put(PleskServer server, string resource, object? body) - { - var request = CreateRequest(server, resource); - - request.Method = Method.Put; - - request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); - - var response = await Client.ExecuteAsync(request); - - if (!response.IsSuccessful) - { - if (response.StatusCode != 0) - { - throw new PleskException( - $"An error occured: ({response.StatusCode}) {response.Content}", - (int)response.StatusCode - ); - } - else - { - throw new Exception($"An internal error occured: {response.ErrorMessage}"); - } - } - } - - private RestRequest CreateRequest(PleskServer pleskServer, string resource) - { - var url = $"{pleskServer.ApiUrl}/" + resource; - - var request = new RestRequest(url); - var ba = Convert.ToBase64String(Encoding.UTF8.GetBytes(pleskServer.ApiKey)); - - request.AddHeader("Content-Type", "application/json"); - request.AddHeader("Accept", "application/json"); - request.AddHeader("Authorization", "Basic " + ba); - - return request; - } -} \ 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/Http/Controllers/Api/Moonlight/OAuth2Controller.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs index 3d1abb4c..aa6c9fe5 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs @@ -18,17 +18,19 @@ public class OAuth2Controller : Controller private readonly DiscordOAuth2Service DiscordOAuth2Service; private readonly UserRepository UserRepository; private readonly UserService UserService; + private readonly DateTimeService DateTimeService; public OAuth2Controller( GoogleOAuth2Service googleOAuth2Service, UserRepository userRepository, UserService userService, - DiscordOAuth2Service discordOAuth2Service) + DiscordOAuth2Service discordOAuth2Service, DateTimeService dateTimeService) { GoogleOAuth2Service = googleOAuth2Service; UserRepository = userRepository; UserService = userService; DiscordOAuth2Service = discordOAuth2Service; + DateTimeService = dateTimeService; } [HttpGet("google")] @@ -63,7 +65,7 @@ public class OAuth2Controller : Controller Response.Cookies.Append("token", token, new () { - Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10)) + Expires = new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10)) }); return Redirect("/"); @@ -121,7 +123,7 @@ public class OAuth2Controller : Controller Response.Cookies.Append("token", token, new () { - Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10)) + Expires = new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10)) }); return Redirect("/"); diff --git a/Moonlight/App/Http/Controllers/Api/Remote/BackupController.cs b/Moonlight/App/Http/Controllers/Api/Remote/BackupController.cs index 46978228..63bbe28b 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/BackupController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/BackupController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Events; using Moonlight.App.Http.Requests.Wings; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; @@ -11,17 +12,17 @@ namespace Moonlight.App.Http.Controllers.Api.Remote; public class BackupController : Controller { private readonly ServerBackupRepository ServerBackupRepository; - private readonly MessageService MessageService; + private readonly EventSystem Event; private readonly NodeRepository NodeRepository; public BackupController( ServerBackupRepository serverBackupRepository, NodeRepository nodeRepository, - MessageService messageService) + EventSystem eventSystem) { ServerBackupRepository = serverBackupRepository; NodeRepository = nodeRepository; - MessageService = messageService; + Event = eventSystem; } [HttpGet("{uuid}")] @@ -57,11 +58,11 @@ public class BackupController : Controller ServerBackupRepository.Update(backup); - await MessageService.Emit($"wings.backups.create", backup); + await Event.Emit($"wings.backups.create", backup); } else { - await MessageService.Emit($"wings.backups.createfailed", backup); + await Event.Emit($"wings.backups.createFailed", backup); ServerBackupRepository.Delete(backup); } @@ -88,7 +89,7 @@ public class BackupController : Controller if (backup == null) return NotFound(); - await MessageService.Emit($"wings.backups.restore", backup); + await Event.Emit($"wings.backups.restore", backup); return NoContent(); } diff --git a/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs index b1cf361c..e6a0dbc9 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs @@ -1,6 +1,7 @@ using Logging.Net; using Microsoft.AspNetCore.Mvc; using Moonlight.App.Database.Entities; +using Moonlight.App.Events; using Moonlight.App.Http.Requests.Daemon; using Moonlight.App.Repositories; using Moonlight.App.Services; @@ -12,13 +13,13 @@ namespace Moonlight.App.Http.Controllers.Api.Remote; public class DdosController : Controller { private readonly NodeRepository NodeRepository; - private readonly MessageService MessageService; + private readonly EventSystem Event; private readonly DdosAttackRepository DdosAttackRepository; - public DdosController(NodeRepository nodeRepository, MessageService messageService, DdosAttackRepository ddosAttackRepository) + public DdosController(NodeRepository nodeRepository, EventSystem eventSystem, DdosAttackRepository ddosAttackRepository) { NodeRepository = nodeRepository; - MessageService = messageService; + Event = eventSystem; DdosAttackRepository = ddosAttackRepository; } @@ -47,7 +48,7 @@ public class DdosController : Controller ddosAttack = DdosAttackRepository.Add(ddosAttack); - await MessageService.Emit("node.ddos", ddosAttack); + await Event.Emit("node.ddos", ddosAttack); return Ok(); } diff --git a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs index 1136bcae..08f4ddc0 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Moonlight.App.Events; using Moonlight.App.Helpers; using Moonlight.App.Http.Resources.Wings; using Moonlight.App.Repositories; @@ -15,18 +16,18 @@ public class ServersController : Controller private readonly WingsServerConverter Converter; private readonly ServerRepository ServerRepository; private readonly NodeRepository NodeRepository; - private readonly MessageService MessageService; + private readonly EventSystem Event; public ServersController( WingsServerConverter converter, ServerRepository serverRepository, NodeRepository nodeRepository, - MessageService messageService) + EventSystem eventSystem) { Converter = converter; ServerRepository = serverRepository; NodeRepository = nodeRepository; - MessageService = messageService; + Event = eventSystem; } [HttpGet] @@ -68,7 +69,7 @@ public class ServersController : Controller totalPages = slice.Length - 1; } - await MessageService.Emit($"wings.{node.Id}.serverlist", node); + await Event.Emit($"wings.{node.Id}.serverList", node); //Logger.Debug($"[BRIDGE] Node '{node.Name}' is requesting server list page {page} with {perPage} items per page"); @@ -97,7 +98,7 @@ public class ServersController : Controller if (token != node.Token) return Unauthorized(); - await MessageService.Emit($"wings.{node.Id}.statereset", node); + await Event.Emit($"wings.{node.Id}.stateReset", node); foreach (var server in ServerRepository .Get() @@ -136,7 +137,7 @@ public class ServersController : Controller if (server == null) return NotFound(); - await MessageService.Emit($"wings.{node.Id}.serverfetch", server); + await Event.Emit($"wings.{node.Id}.serverFetch", server); try //TODO: Remove { @@ -169,7 +170,7 @@ public class ServersController : Controller if (server == null) return NotFound(); - await MessageService.Emit($"wings.{node.Id}.serverinstallfetch", server); + await Event.Emit($"wings.{node.Id}.serverInstallFetch", server); return new WingsServerInstall() { @@ -202,8 +203,8 @@ public class ServersController : Controller server.Installing = false; ServerRepository.Update(server); - await MessageService.Emit($"wings.{node.Id}.serverinstallcomplete", server); - await MessageService.Emit($"server.{server.Uuid}.installcomplete", server); + await Event.Emit($"wings.{node.Id}.serverInstallComplete", server); + await Event.Emit($"server.{server.Uuid}.installComplete", server); return Ok(); } diff --git a/Moonlight/App/MessageSystem/MessageSender.cs b/Moonlight/App/MessageSystem/MessageSender.cs deleted file mode 100644 index a564eb58..00000000 --- a/Moonlight/App/MessageSystem/MessageSender.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Diagnostics; -using Logging.Net; - -namespace Moonlight.App.MessageSystem; - -public class MessageSender -{ - private readonly List Subscribers; - - public bool Debug { get; set; } - public TimeSpan TookToLongTime { get; set; } = TimeSpan.FromSeconds(1); - - public MessageSender() - { - Subscribers = new(); - } - - public void Subscribe(string name, object bind, Func method) - { - lock (Subscribers) - { - Subscribers.Add(new () - { - Name = name, - Action = method, - Type = typeof(T), - Bind = bind - }); - } - - if(Debug) - Logger.Debug($"{bind} subscribed to '{name}'"); - } - - public void Unsubscribe(string name, object bind) - { - lock (Subscribers) - { - Subscribers.RemoveAll(x => x.Bind == bind); - } - - if(Debug) - Logger.Debug($"{bind} unsubscribed from '{name}'"); - } - - public Task Emit(string name, object? value, bool disableWarning = false) - { - lock (Subscribers) - { - foreach (var subscriber in Subscribers) - { - if (subscriber.Name == name) - { - var stopWatch = new Stopwatch(); - stopWatch.Start(); - - var del = (Delegate)subscriber.Action; - - ((Task)del.DynamicInvoke(value)!).Wait(); - - stopWatch.Stop(); - - if (!disableWarning) - { - if (stopWatch.Elapsed.TotalMilliseconds > TookToLongTime.TotalMilliseconds) - { - Logger.Warn( - $"Subscriber {subscriber.Type.Name} for event '{name}' took long to process. {stopWatch.Elapsed.TotalMilliseconds}ms"); - } - } - - if (Debug) - { - Logger.Debug( - $"Subscriber {subscriber.Type.Name} for event '{name}' took {stopWatch.Elapsed.TotalMilliseconds}ms"); - } - } - } - } - - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/Moonlight/App/MessageSystem/MessageSubscriber.cs b/Moonlight/App/MessageSystem/MessageSubscriber.cs deleted file mode 100644 index b435d81f..00000000 --- a/Moonlight/App/MessageSystem/MessageSubscriber.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Moonlight.App.MessageSystem; - -public class MessageSubscriber -{ - public string Name { get; set; } - public object Action { get; set; } - public Type Type { get; set; } - public object Bind { get; set; } -} \ 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/Models/Forms/DomainOrderDataModel.cs b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs index c49dd8e3..b3b8a93f 100644 --- a/Moonlight/App/Models/Forms/DomainOrderDataModel.cs +++ b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs @@ -7,7 +7,7 @@ public class DomainOrderDataModel { [Required(ErrorMessage = "You need to specify a name")] [MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")] - [RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")] + [RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only consist of lower case characters or numbers")] public string Name { get; set; } = ""; [Required(ErrorMessage = "You need to specify a shared domain")] diff --git a/Moonlight/App/Models/Forms/ServerEditDataModel.cs b/Moonlight/App/Models/Forms/ServerEditDataModel.cs new file mode 100644 index 00000000..22adcbaf --- /dev/null +++ b/Moonlight/App/Models/Forms/ServerEditDataModel.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class ServerEditDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "You need to specify a user")] + public User Owner { get; set; } + + [Required(ErrorMessage = "You need to specify the cpu cores")] + public int Cpu { get; set; } + + [Required(ErrorMessage = "You need to specify the memory")] + public long Memory { get; set; } + + [Required(ErrorMessage = "You need to specify the disk")] + public long Disk { get; set; } + + public string OverrideStartup { get; set; } + + public int DockerImageIndex { get; set; } + + public bool IsCleanupException { get; set; } +} \ 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/Repositories/PleskServerRepository.cs b/Moonlight/App/Repositories/PleskServerRepository.cs deleted file mode 100644 index 53742635..00000000 --- a/Moonlight/App/Repositories/PleskServerRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database; -using Moonlight.App.Database.Entities; - -namespace Moonlight.App.Repositories; - -public class PleskServerRepository : IDisposable -{ - private readonly DataContext DataContext; - - public PleskServerRepository(DataContext dataContext) - { - DataContext = dataContext; - } - - public DbSet Get() - { - return DataContext.PleskServers; - } - - public PleskServer Add(PleskServer pleskServer) - { - var x = DataContext.PleskServers.Add(pleskServer); - DataContext.SaveChanges(); - return x.Entity; - } - - public void Update(PleskServer pleskServer) - { - DataContext.PleskServers.Update(pleskServer); - DataContext.SaveChanges(); - } - - public void Delete(PleskServer pleskServer) - { - DataContext.PleskServers.Remove(pleskServer); - DataContext.SaveChanges(); - } - - public void Dispose() - { - DataContext.Dispose(); - } -} \ 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/Repositories/StatisticsRepository.cs b/Moonlight/App/Repositories/StatisticsRepository.cs index f33751b7..69630abe 100644 --- a/Moonlight/App/Repositories/StatisticsRepository.cs +++ b/Moonlight/App/Repositories/StatisticsRepository.cs @@ -1,16 +1,19 @@ using Microsoft.EntityFrameworkCore; using Moonlight.App.Database; using Moonlight.App.Database.Entities; +using Moonlight.App.Services; namespace Moonlight.App.Repositories; public class StatisticsRepository : IDisposable { private readonly DataContext DataContext; + private readonly DateTimeService DateTimeService; - public StatisticsRepository(DataContext dataContext) + public StatisticsRepository(DataContext dataContext, DateTimeService dateTimeService) { DataContext = dataContext; + DateTimeService = dateTimeService; } public DbSet Get() @@ -27,7 +30,7 @@ public class StatisticsRepository : IDisposable public StatisticsData Add(string chart, double value) { - return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTime.Now}); + return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTimeService.GetCurrent()}); } public void Dispose() diff --git a/Moonlight/App/Repositories/SupportMessageRepository.cs b/Moonlight/App/Repositories/SupportMessageRepository.cs deleted file mode 100644 index 1f6ec2e7..00000000 --- a/Moonlight/App/Repositories/SupportMessageRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database; -using Moonlight.App.Database.Entities; - -namespace Moonlight.App.Repositories; - -public class SupportMessageRepository : IDisposable -{ - private readonly DataContext DataContext; - - public SupportMessageRepository(DataContext dataContext) - { - DataContext = dataContext; - } - - public DbSet Get() - { - return DataContext.SupportMessages; - } - - public SupportMessage Add(SupportMessage message) - { - var x = DataContext.SupportMessages.Add(message); - DataContext.SaveChanges(); - return x.Entity; - } - - public void Update(SupportMessage message) - { - DataContext.SupportMessages.Update(message); - DataContext.SaveChanges(); - } - - public void Delete(SupportMessage message) - { - DataContext.SupportMessages.Remove(message); - DataContext.SaveChanges(); - } - - public void Dispose() - { - DataContext.Dispose(); - } -} \ No newline at end of file diff --git a/Moonlight/App/Repositories/WebsiteRepository.cs b/Moonlight/App/Repositories/WebsiteRepository.cs deleted file mode 100644 index 4a45b5da..00000000 --- a/Moonlight/App/Repositories/WebsiteRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database; -using Moonlight.App.Database.Entities; - -namespace Moonlight.App.Repositories; - -public class WebsiteRepository : IDisposable -{ - private readonly DataContext DataContext; - - public WebsiteRepository(DataContext dataContext) - { - DataContext = dataContext; - } - - public DbSet Get() - { - return DataContext.Websites; - } - - public Website Add(Website website) - { - var x = DataContext.Websites.Add(website); - DataContext.SaveChanges(); - return x.Entity; - } - - public void Update(Website website) - { - DataContext.Websites.Update(website); - DataContext.SaveChanges(); - } - - public void Delete(Website website) - { - DataContext.Websites.Remove(website); - DataContext.SaveChanges(); - } - - public void Dispose() - { - DataContext.Dispose(); - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/CleanupService.cs b/Moonlight/App/Services/CleanupService.cs index 42e046ce..1ccb78c9 100644 --- a/Moonlight/App/Services/CleanupService.cs +++ b/Moonlight/App/Services/CleanupService.cs @@ -7,6 +7,7 @@ using Moonlight.App.Models.Wings; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; using Logging.Net; +using Moonlight.App.Events; using Newtonsoft.Json; namespace Moonlight.App.Services; @@ -23,21 +24,24 @@ public class CleanupService #endregion private readonly ConfigService ConfigService; - private readonly MessageService MessageService; + private readonly DateTimeService DateTimeService; + private readonly EventSystem Event; private readonly IServiceScopeFactory ServiceScopeFactory; private readonly PeriodicTimer Timer; public CleanupService( ConfigService configService, IServiceScopeFactory serviceScopeFactory, - MessageService messageService) + DateTimeService dateTimeService, + EventSystem eventSystem) { ServiceScopeFactory = serviceScopeFactory; - MessageService = messageService; + DateTimeService = dateTimeService; ConfigService = configService; + Event = eventSystem; - StartedAt = DateTime.Now; - CompletedAt = DateTime.Now; + StartedAt = DateTimeService.GetCurrent(); + CompletedAt = DateTimeService.GetCurrent(); IsRunning = false; var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup"); @@ -145,7 +149,7 @@ public class CleanupService ServersRunning++; } - await MessageService.Emit("cleanup.updated", null); + await Event.Emit("cleanup.updated"); } } else @@ -175,7 +179,7 @@ public class CleanupService ServersRunning++; } - await MessageService.Emit("cleanup.updated", null); + await Event.Emit("cleanup.updated"); } } } @@ -196,7 +200,7 @@ public class CleanupService IsRunning = false; CleanupsPerformed++; - await MessageService.Emit("cleanup.updated", null); + await Event.Emit("cleanup.updated"); } } 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/DateTimeService.cs b/Moonlight/App/Services/DateTimeService.cs new file mode 100644 index 00000000..40b0231e --- /dev/null +++ b/Moonlight/App/Services/DateTimeService.cs @@ -0,0 +1,31 @@ +using Moonlight.App.Helpers; + +namespace Moonlight.App.Services; + +public class DateTimeService +{ + public long GetCurrentUnix() + { + return new DateTimeOffset(GetCurrent()).ToUnixTimeMilliseconds(); + } + + public long GetCurrentUnixSeconds() + { + return new DateTimeOffset(GetCurrent()).ToUnixTimeSeconds(); + } + + public DateTime GetCurrent() + { + return DateTime.UtcNow; + } + + public string GetDate() + { + return Formatter.FormatDateOnly(GetCurrent()); + } + + public string GetDateTime() + { + return Formatter.FormatDate(GetCurrent()); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DiscordNotificationService.cs b/Moonlight/App/Services/DiscordNotificationService.cs new file mode 100644 index 00000000..41ea9a0b --- /dev/null +++ b/Moonlight/App/Services/DiscordNotificationService.cs @@ -0,0 +1,99 @@ +using Discord; +using Discord.Webhook; +using Logging.Net; +using Moonlight.App.Database.Entities; +using Moonlight.App.Events; + +namespace Moonlight.App.Services; + +public class DiscordNotificationService +{ + private readonly EventSystem Event; + private readonly ResourceService ResourceService; + private readonly DiscordWebhookClient Client; + private readonly string AppUrl; + + public DiscordNotificationService( + EventSystem eventSystem, + ConfigService configService, + ResourceService resourceService) + { + Event = eventSystem; + ResourceService = resourceService; + + var config = configService.GetSection("Moonlight").GetSection("DiscordNotifications"); + + if (config.GetValue("Enable")) + { + Logger.Info("Discord notifications enabled"); + + Client = new(config.GetValue("WebHook")); + AppUrl = configService.GetSection("Moonlight").GetValue("AppUrl"); + + Event.On("supportChat.new", this, OnNewSupportChat); + Event.On("supportChat.message", this, OnSupportChatMessage); + Event.On("supportChat.close", this, OnSupportChatClose); + } + else + { + Logger.Info("Discord notifications disabled"); + } + } + + private async Task OnSupportChatClose(User user) + { + await SendNotification("", builder => + { + builder.Title = "A new support chat has been marked as closed"; + builder.Color = Color.Red; + builder.AddField("Email", user.Email); + builder.AddField("Firstname", user.FirstName); + builder.AddField("Lastname", user.LastName); + builder.Url = $"{AppUrl}/admin/support/view/{user.Id}"; + }); + } + + private async Task OnSupportChatMessage(SupportChatMessage message) + { + if(message.Sender == null) + return; + + await SendNotification("", builder => + { + builder.Title = "New message in support chat"; + builder.Color = Color.Blue; + builder.AddField("Message", message.Content); + builder.Author = new EmbedAuthorBuilder() + .WithName($"{message.Sender.FirstName} {message.Sender.LastName}") + .WithIconUrl(ResourceService.Avatar(message.Sender)); + builder.Url = $"{AppUrl}/admin/support/view/{message.Recipient.Id}"; + }); + } + + private async Task OnNewSupportChat(User user) + { + await SendNotification("", builder => + { + builder.Title = "A new support chat has been marked as active"; + builder.Color = Color.Green; + builder.AddField("Email", user.Email); + builder.AddField("Firstname", user.FirstName); + builder.AddField("Lastname", user.LastName); + builder.Url = $"{AppUrl}/admin/support/view/{user.Id}"; + }); + } + + private async Task SendNotification(string content, Action? embed = null) + { + var e = new EmbedBuilder(); + embed?.Invoke(e); + + await Client.SendMessageAsync( + content, + false, + new []{e.Build()}, + "Moonlight Notification", + ResourceService.Image("logo.svg") + ); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/FabricService.cs b/Moonlight/App/Services/FabricService.cs new file mode 100644 index 00000000..650ce155 --- /dev/null +++ b/Moonlight/App/Services/FabricService.cs @@ -0,0 +1,96 @@ +using System.Text; +using Moonlight.App.Helpers; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Moonlight.App.Services; + +public class FabricService +{ + private readonly HttpClient Client; + + public FabricService() + { + Client = new(); + } + + public async Task GetLatestInstallerVersion() + { + var data = await Client + .GetStringAsync("https://meta.fabricmc.net/v2/versions/installer"); + + var x = JsonConvert.DeserializeObject(data) ?? Array.Empty(); + + var stableVersions = new List(); + + foreach (var y in x) + { + var section = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + y.Root.ToString() + ) + ) + ).Build(); + + if (section.GetValue("stable")) + { + stableVersions.Add(section.GetValue("version")); + } + } + + return ParseHelper.GetHighestVersion(stableVersions.ToArray()); + } + + public async Task GetLatestLoaderVersion() + { + var data = await Client + .GetStringAsync("https://meta.fabricmc.net/v2/versions/loader"); + + var x = JsonConvert.DeserializeObject(data) ?? Array.Empty(); + + var stableVersions = new List(); + + foreach (var y in x) + { + var section = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + y.Root.ToString() + ) + ) + ).Build(); + + if (section.GetValue("stable")) + { + stableVersions.Add(section.GetValue("version")); + } + } + + return ParseHelper.GetHighestVersion(stableVersions.ToArray()); + } + public async Task GetGameVersions() + { + var data = await Client + .GetStringAsync("https://meta.fabricmc.net/v2/versions/game"); + + var x = JsonConvert.DeserializeObject(data) ?? Array.Empty(); + + var stableVersions = new List(); + + foreach (var y in x) + { + var section = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + y.Root.ToString() + ) + ) + ).Build(); + + if (section.GetValue("stable")) + { + stableVersions.Add(section.GetValue("version")); + } + } + + return stableVersions.ToArray(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/FileDownloadService.cs b/Moonlight/App/Services/FileDownloadService.cs new file mode 100644 index 00000000..ad85c010 --- /dev/null +++ b/Moonlight/App/Services/FileDownloadService.cs @@ -0,0 +1,33 @@ +using System.Text; +using Microsoft.JSInterop; + +namespace Moonlight.App.Services; + +public class FileDownloadService +{ + private readonly IJSRuntime JSRuntime; + + public FileDownloadService(IJSRuntime jsRuntime) + { + JSRuntime = jsRuntime; + } + + public async Task DownloadStream(string fileName, Stream stream) + { + using var streamRef = new DotNetStreamReference(stream); + + await JSRuntime.InvokeVoidAsync("moonlight.downloads.downloadStream", fileName, streamRef); + } + + public async Task DownloadBytes(string fileName, byte[] bytes) + { + var ms = new MemoryStream(bytes); + + await DownloadStream(fileName, ms); + } + + public async Task DownloadString(string fileName, string content) + { + await DownloadBytes(fileName, Encoding.UTF8.GetBytes(content)); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/ForgeService.cs b/Moonlight/App/Services/ForgeService.cs new file mode 100644 index 00000000..6171ef44 --- /dev/null +++ b/Moonlight/App/Services/ForgeService.cs @@ -0,0 +1,37 @@ +using System.Text; +using Moonlight.App.Helpers; + +namespace Moonlight.App.Services; + +public class ForgeService +{ + private readonly HttpClient Client; + + public ForgeService() + { + Client = new(); + } + + // Key: 1.9.4-recommended Value: 12.17.0.2317 + public async Task> GetVersions() + { + var data = await Client.GetStringAsync( + "https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"); + + var json = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + data + ) + ) + ).Build(); + + var d = new Dictionary(); + + foreach (var section in json.GetSection("promos").GetChildren()) + { + d.Add(section.Key, section.Value!); + } + + return d; + } +} \ No newline at end of file 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/App/Services/MailService.cs b/Moonlight/App/Services/MailService.cs index 26764603..9bcab325 100644 --- a/Moonlight/App/Services/MailService.cs +++ b/Moonlight/App/Services/MailService.cs @@ -1,9 +1,11 @@ using System.Net; using System.Net.Mail; using Logging.Net; +using MimeKit; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; using Moonlight.App.Helpers; +using SmtpClient = MailKit.Net.Smtp.SmtpClient; namespace Moonlight.App.Services; @@ -13,6 +15,7 @@ public class MailService private readonly string Password; private readonly string Email; private readonly int Port; + private readonly bool Ssl; public MailService(ConfigService configService) { @@ -24,6 +27,7 @@ public class MailService Password = mailConfig.GetValue("Password"); Email = mailConfig.GetValue("Email"); Port = mailConfig.GetValue("Port"); + Ssl = mailConfig.GetValue("Ssl"); } public async Task SendMail( @@ -54,20 +58,24 @@ public class MailService { using var client = new SmtpClient(); - client.Host = Server; - client.Port = Port; - client.EnableSsl = true; - client.Credentials = new NetworkCredential(Email, Password); - - await client.SendMailAsync(new MailMessage() + var mailMessage = new MimeMessage(); + mailMessage.From.Add(new MailboxAddress(Email, Email)); + mailMessage.To.Add(new MailboxAddress(user.Email, user.Email)); + mailMessage.Subject = $"Hey {user.FirstName}, there are news from moonlight"; + + var body = new BodyBuilder { - From = new MailAddress(Email), - Sender = new MailAddress(Email), - Body = parsed, - IsBodyHtml = true, - Subject = $"Hey {user.FirstName}, there are news from moonlight", - To = { new MailAddress(user.Email) } - }); + HtmlBody = parsed + }; + mailMessage.Body = body.ToMessageBody(); + + using (var smtpClient = new SmtpClient()) + { + await smtpClient.ConnectAsync(Server, Port, Ssl); + await smtpClient.AuthenticateAsync(Email, Password); + await smtpClient.SendAsync(mailMessage); + await smtpClient.DisconnectAsync(true); + } } catch (Exception e) { diff --git a/Moonlight/App/Services/MessageService.cs b/Moonlight/App/Services/MessageService.cs deleted file mode 100644 index 1a4c8535..00000000 --- a/Moonlight/App/Services/MessageService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Moonlight.App.MessageSystem; - -namespace Moonlight.App.Services; - -public class MessageService : MessageSender -{ - public MessageService() - { - Debug = false; - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 2e46eb44..d61ae7d4 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Moonlight.App.Database; using Moonlight.App.Database.Entities; +using Moonlight.App.Events; using Moonlight.App.Exceptions; using Moonlight.App.Helpers; using Moonlight.App.Helpers.Files; @@ -21,8 +22,8 @@ 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; private readonly ConfigService ConfigService; private readonly WingsJwtHelper WingsJwtHelper; @@ -30,6 +31,8 @@ public class ServerService private readonly AuditLogService AuditLogService; private readonly ErrorLogService ErrorLogService; private readonly NodeService NodeService; + private readonly DateTimeService DateTimeService; + private readonly EventSystem Event; public ServerService( ServerRepository serverRepository, @@ -37,21 +40,22 @@ public class ServerService UserRepository userRepository, ImageRepository imageRepository, NodeRepository nodeRepository, - MessageService messageService, UserService userService, ConfigService configService, WingsJwtHelper wingsJwtHelper, SecurityLogService securityLogService, AuditLogService auditLogService, ErrorLogService errorLogService, - NodeService nodeService) + NodeService nodeService, + NodeAllocationRepository nodeAllocationRepository, + DateTimeService dateTimeService, + EventSystem eventSystem) { ServerRepository = serverRepository; WingsApiHelper = wingsApiHelper; UserRepository = userRepository; ImageRepository = imageRepository; NodeRepository = nodeRepository; - MessageService = messageService; UserService = userService; ConfigService = configService; WingsJwtHelper = wingsJwtHelper; @@ -59,6 +63,9 @@ public class ServerService AuditLogService = auditLogService; ErrorLogService = errorLogService; NodeService = nodeService; + NodeAllocationRepository = nodeAllocationRepository; + DateTimeService = dateTimeService; + Event = eventSystem; } private Server EnsureNodeData(Server s) @@ -112,9 +119,9 @@ public class ServerService var backup = new ServerBackup() { - Name = $"Created at {DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}", + Name = $"Created at {DateTimeService.GetCurrent().ToShortDateString()} {DateTimeService.GetCurrent().ToShortTimeString()}", Uuid = Guid.NewGuid(), - CreatedAt = DateTime.Now, + CreatedAt = DateTimeService.GetCurrent(), Created = false }; @@ -186,15 +193,27 @@ 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); ServerRepository.Update(serverData); - await MessageService.Emit("wings.backups.delete", backup); + await Event.Emit("wings.backups.delete", backup); await AuditLogService.Log(AuditLogType.DeleteBackup, x => @@ -244,7 +263,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() @@ -256,32 +275,21 @@ public class ServerService .Include(x => x.DockerImages) .First(x => x.Id == i.Id); - Node node; + var allocations = image.Allocations; - 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) { @@ -372,7 +380,7 @@ public class ServerService var user = await UserService.SftpLogin(id, password); - if (server.Owner.Id == user.Id) + if (server.Owner.Id == user.Id || user.Admin) { return server; } diff --git a/Moonlight/App/Services/Sessions/IdentityService.cs b/Moonlight/App/Services/Sessions/IdentityService.cs index f35519e1..ef98937f 100644 --- a/Moonlight/App/Services/Sessions/IdentityService.cs +++ b/Moonlight/App/Services/Sessions/IdentityService.cs @@ -4,6 +4,7 @@ using JWT.Builder; using JWT.Exceptions; using Logging.Net; using Moonlight.App.Database.Entities; +using Moonlight.App.Helpers; using Moonlight.App.Models.Misc; using Moonlight.App.Repositories; using Moonlight.App.Services.LogServices; @@ -123,9 +124,9 @@ public class IdentityService return null; } - var issuedAt = DateTimeOffset.FromUnixTimeSeconds(iat).DateTime; - - if (issuedAt < user.TokenValidTime.ToUniversalTime()) + var iatD = DateTimeOffset.FromUnixTimeSeconds(iat).ToUniversalTime().DateTime; + + if (iatD < user.TokenValidTime) return null; UserCache = user; diff --git a/Moonlight/App/Services/Sessions/SessionService.cs b/Moonlight/App/Services/Sessions/SessionService.cs index 1ecbd67d..f7b97cef 100644 --- a/Moonlight/App/Services/Sessions/SessionService.cs +++ b/Moonlight/App/Services/Sessions/SessionService.cs @@ -12,6 +12,7 @@ public class SessionService private readonly IdentityService IdentityService; private readonly NavigationManager NavigationManager; private readonly AlertService AlertService; + private readonly DateTimeService DateTimeService; private Session? OwnSession; @@ -19,12 +20,14 @@ public class SessionService SessionRepository sessionRepository, IdentityService identityService, NavigationManager navigationManager, - AlertService alertService) + AlertService alertService, + DateTimeService dateTimeService) { SessionRepository = sessionRepository; IdentityService = identityService; NavigationManager = navigationManager; AlertService = alertService; + DateTimeService = dateTimeService; } public async Task Register() @@ -36,7 +39,7 @@ public class SessionService Ip = IdentityService.GetIp(), Url = NavigationManager.Uri, Device = IdentityService.GetDevice(), - CreatedAt = DateTime.Now, + CreatedAt = DateTimeService.GetCurrent(), User = user, Navigation = NavigationManager, AlertService = AlertService @@ -64,10 +67,8 @@ public class SessionService { foreach (var session in SessionRepository.Get()) { - if (session.User.Id == user.Id) - { + if(session.User != null && session.User.Id == user.Id) session.Navigation.NavigateTo(session.Navigation.Uri, true); - } } } } \ No newline at end of file diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs index 57aa4bc8..38d013a0 100644 --- a/Moonlight/App/Services/SmartDeployService.cs +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -6,18 +6,20 @@ namespace Moonlight.App.Services; public class SmartDeployService { private readonly NodeRepository NodeRepository; - private readonly PleskServerRepository PleskServerRepository; - private readonly WebsiteService WebsiteService; + private readonly Repository CloudPanelRepository; + private readonly WebSpaceService WebSpaceService; private readonly NodeService NodeService; public SmartDeployService( NodeRepository nodeRepository, - NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService) + NodeService nodeService, + WebSpaceService webSpaceService, + Repository cloudPanelRepository) { NodeRepository = nodeRepository; NodeService = nodeService; - PleskServerRepository = pleskServerRepository; - WebsiteService = websiteService; + WebSpaceService = webSpaceService; + CloudPanelRepository = cloudPanelRepository; } public async Task GetNode() @@ -38,16 +40,14 @@ public class SmartDeployService return data.MaxBy(x => x.Value).Key; } - public async Task GetPleskServer() + public async Task GetCloudPanel() { - var result = new List(); + var result = new List(); - foreach (var pleskServer in PleskServerRepository.Get().ToArray()) + foreach (var cloudPanel in CloudPanelRepository.Get().ToArray()) { - if (await WebsiteService.IsHostUp(pleskServer)) - { - result.Add(pleskServer); - } + if (await WebSpaceService.IsHostUp(cloudPanel)) + result.Add(cloudPanel); } return result.FirstOrDefault(); diff --git a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs index 3098e5d1..139ece7b 100644 --- a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs +++ b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs @@ -1,57 +1,78 @@ -using Moonlight.App.Database; +using Logging.Net; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; using Moonlight.App.Repositories; +using Moonlight.App.Services.Sessions; namespace Moonlight.App.Services.Statistics; public class StatisticsCaptureService { - private readonly DataContext DataContext; - private readonly ConfigService ConfigService; - private readonly StatisticsRepository StatisticsRepository; private readonly IServiceScopeFactory ServiceScopeFactory; - private readonly WebsiteService WebsiteService; - private readonly PleskServerRepository PleskServerRepository; - private PeriodicTimer Timer; + private readonly DateTimeService DateTimeService; + private readonly PeriodicTimer Timer; - public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService) + public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService, DateTimeService dateTimeService) { ServiceScopeFactory = serviceScopeFactory; - var provider = ServiceScopeFactory.CreateScope().ServiceProvider; - - DataContext = provider.GetRequiredService(); - ConfigService = configService; - StatisticsRepository = provider.GetRequiredService(); - WebsiteService = provider.GetRequiredService(); - PleskServerRepository = provider.GetRequiredService(); + DateTimeService = dateTimeService; - var config = ConfigService.GetSection("Moonlight").GetSection("Statistics"); + var config = configService + .GetSection("Moonlight") + .GetSection("Statistics"); + if(!config.GetValue("Enabled")) return; - - var _period = config.GetValue("Wait"); - var period = TimeSpan.FromMinutes(_period); + + var period = TimeSpan.FromMinutes(config.GetValue("Wait")); Timer = new(period); + Logger.Info("Starting statistics system"); Task.Run(Run); } private async Task Run() { - while (await Timer.WaitForNextTickAsync()) + try { - StatisticsRepository.Add("statistics.usersCount", DataContext.Users.Count()); - StatisticsRepository.Add("statistics.serversCount", DataContext.Servers.Count()); - StatisticsRepository.Add("statistics.domainsCount", DataContext.Domains.Count()); - StatisticsRepository.Add("statistics.websitesCount", DataContext.Websites.Count()); - - int databases = 0; - - await foreach (var pleskServer in PleskServerRepository.Get()) + while (await Timer.WaitForNextTickAsync()) { - databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount; + Logger.Warn("Creating statistics"); + + using var scope = ServiceScopeFactory.CreateScope(); + + var statisticsRepo = scope.ServiceProvider.GetRequiredService>(); + var usersRepo = scope.ServiceProvider.GetRequiredService>(); + var serversRepo = scope.ServiceProvider.GetRequiredService>(); + var domainsRepo = scope.ServiceProvider.GetRequiredService>(); + var webspacesRepo = scope.ServiceProvider.GetRequiredService>(); + var databasesRepo = scope.ServiceProvider.GetRequiredService>(); + var sessionService = scope.ServiceProvider.GetRequiredService(); + + void AddEntry(string chart, int value) + { + statisticsRepo!.Add(new StatisticsData() + { + Chart = chart, + Value = value, + Date = DateTimeService.GetCurrent() + }); + } + + AddEntry("usersCount", usersRepo.Get().Count()); + AddEntry("serversCount", serversRepo.Get().Count()); + AddEntry("domainsCount", domainsRepo.Get().Count()); + AddEntry("webspacesCount", webspacesRepo.Get().Count()); + AddEntry("databasesCount", databasesRepo.Get().Count()); + AddEntry("sessionsCount", sessionService.GetAll().Length); } - StatisticsRepository.Add("statistics.databasesCount", databases); + Logger.Log("Statistics are weird"); + } + catch (Exception e) + { + Logger.Error("An unexpected error occured while capturing statistics"); + Logger.Error(e); } } } \ No newline at end of file diff --git a/Moonlight/App/Services/Statistics/StatisticsViewService.cs b/Moonlight/App/Services/Statistics/StatisticsViewService.cs index 329445fe..dfb68f01 100644 --- a/Moonlight/App/Services/Statistics/StatisticsViewService.cs +++ b/Moonlight/App/Services/Statistics/StatisticsViewService.cs @@ -7,15 +7,17 @@ namespace Moonlight.App.Services.Statistics; public class StatisticsViewService { private readonly StatisticsRepository StatisticsRepository; + private readonly DateTimeService DateTimeService; - public StatisticsViewService(StatisticsRepository statisticsRepository) + public StatisticsViewService(StatisticsRepository statisticsRepository, DateTimeService dateTimeService) { StatisticsRepository = statisticsRepository; + DateTimeService = dateTimeService; } public StatisticsData[] GetData(string chart, StatisticsTimeSpan timeSpan) { - var startDate = DateTime.Now - TimeSpan.FromHours((int)timeSpan); + var startDate = DateTimeService.GetCurrent() - TimeSpan.FromHours((int)timeSpan); var objs = StatisticsRepository.Get().Where(x => x.Date > startDate && x.Chart == chart); diff --git a/Moonlight/App/Services/SubscriptionService.cs b/Moonlight/App/Services/SubscriptionService.cs index de23692a..2dcc797c 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() @@ -90,13 +89,15 @@ 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(); 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/App/Services/Support/SupportAdminService.cs b/Moonlight/App/Services/Support/SupportAdminService.cs deleted file mode 100644 index 2dabdef1..00000000 --- a/Moonlight/App/Services/Support/SupportAdminService.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Moonlight.App.Database.Entities; -using Moonlight.App.Services.Sessions; - -namespace Moonlight.App.Services.Support; - -public class SupportAdminService -{ - private readonly SupportServerService SupportServerService; - private readonly IdentityService IdentityService; - private readonly MessageService MessageService; - - public EventHandler OnNewMessage; - - public EventHandler OnUpdateTyping; - private List TypingUsers = new(); - - private User Self; - private User Recipient; - - public SupportAdminService( - SupportServerService supportServerService, - IdentityService identityService, - MessageService messageService) - { - SupportServerService = supportServerService; - IdentityService = identityService; - MessageService = messageService; - } - - public async Task Start(User user) - { - Self = (await IdentityService.Get())!; - Recipient = user; - - MessageService.Subscribe( - $"support.{Recipient.Id}.message", - this, - message => - { - OnNewMessage?.Invoke(this, message); - - return Task.CompletedTask; - }); - - MessageService.Subscribe( - $"support.{Self.Id}.typing", - this, - user => - { - HandleTyping(user); - return Task.CompletedTask; - }); - } - - #region Typing - - private void HandleTyping(User user) - { - var name = $"{user.FirstName} {user.LastName}"; - - lock (TypingUsers) - { - if (!TypingUsers.Contains(name)) - { - TypingUsers.Add(name); - OnUpdateTyping!.Invoke(this, null!); - - Task.Run(async () => - { - await Task.Delay(TimeSpan.FromSeconds(5)); - - if (TypingUsers.Contains(name)) - { - TypingUsers.Remove(name); - OnUpdateTyping!.Invoke(this, null!); - } - }); - } - } - } - - public string[] GetTypingUsers() - { - lock (TypingUsers) - { - return TypingUsers.ToArray(); - } - } - - public Task TriggerTyping() - { - Task.Run(async () => - { - await MessageService.Emit($"support.{Recipient.Id}.admintyping", Self); - }); - - return Task.CompletedTask; - } - - #endregion - - public async Task GetMessages() - { - return await SupportServerService.GetMessages(Recipient); - } - - public async Task SendMessage(string content) - { - var message = new SupportMessage() - { - Message = content - }; - - await SupportServerService.SendMessage( - Recipient, - message, - Self, - true - ); - } - - public async Task Close() - { - await SupportServerService.Close(Recipient); - } - - public void Dispose() - { - MessageService.Unsubscribe($"support.{Recipient.Id}.message", this); - MessageService.Unsubscribe($"support.{Recipient.Id}.typing", this); - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/Support/SupportClientService.cs b/Moonlight/App/Services/Support/SupportClientService.cs deleted file mode 100644 index 5b4f54dc..00000000 --- a/Moonlight/App/Services/Support/SupportClientService.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Moonlight.App.Database.Entities; -using Moonlight.App.Services.Sessions; - -namespace Moonlight.App.Services.Support; - -public class SupportClientService : IDisposable -{ - private readonly SupportServerService SupportServerService; - private readonly IdentityService IdentityService; - private readonly MessageService MessageService; - - public EventHandler OnNewMessage; - - public EventHandler OnUpdateTyping; - private List TypingUsers = new(); - - private User Self; - - public SupportClientService( - SupportServerService supportServerService, - IdentityService identityService, - MessageService messageService) - { - SupportServerService = supportServerService; - IdentityService = identityService; - MessageService = messageService; - } - - public async Task Start() - { - Self = (await IdentityService.Get())!; - - MessageService.Subscribe( - $"support.{Self.Id}.message", - this, - message => - { - OnNewMessage?.Invoke(this, message); - - return Task.CompletedTask; - }); - - MessageService.Subscribe( - $"support.{Self.Id}.admintyping", - this, - user => - { - HandleTyping(user); - return Task.CompletedTask; - }); - } - - #region Typing - - private void HandleTyping(User user) - { - var name = $"{user.FirstName} {user.LastName}"; - - lock (TypingUsers) - { - if (!TypingUsers.Contains(name)) - { - TypingUsers.Add(name); - OnUpdateTyping!.Invoke(this, null!); - - Task.Run(async () => - { - await Task.Delay(TimeSpan.FromSeconds(5)); - - if (TypingUsers.Contains(name)) - { - TypingUsers.Remove(name); - OnUpdateTyping!.Invoke(this, null!); - } - }); - } - } - } - - public string[] GetTypingUsers() - { - lock (TypingUsers) - { - return TypingUsers.ToArray(); - } - } - - public Task TriggerTyping() - { - Task.Run(async () => - { - await MessageService.Emit($"support.{Self.Id}.typing", Self); - }); - - return Task.CompletedTask; - } - - #endregion - - public async Task GetMessages() - { - return await SupportServerService.GetMessages(Self); - } - - public async Task SendMessage(string content) - { - var message = new SupportMessage() - { - Message = content - }; - - await SupportServerService.SendMessage( - Self, - message, - Self - ); - } - - public void Dispose() - { - MessageService.Unsubscribe($"support.{Self.Id}.message", this); - MessageService.Unsubscribe($"support.{Self.Id}.admintyping", this); - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/Support/SupportServerService.cs b/Moonlight/App/Services/Support/SupportServerService.cs deleted file mode 100644 index f2b22b7f..00000000 --- a/Moonlight/App/Services/Support/SupportServerService.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Logging.Net; -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database.Entities; -using Moonlight.App.Repositories; - -namespace Moonlight.App.Services.Support; - -public class SupportServerService : IDisposable -{ - private SupportMessageRepository SupportMessageRepository; - private MessageService MessageService; - private UserRepository UserRepository; - private readonly IServiceScopeFactory ServiceScopeFactory; - private IServiceScope ServiceScope; - - public SupportServerService(IServiceScopeFactory serviceScopeFactory) - { - ServiceScopeFactory = serviceScopeFactory; - - Task.Run(Run); - } - - public async Task SendMessage(User r, SupportMessage message, User s, bool isSupport = false) - { - var recipient = UserRepository.Get().First(x => x.Id == r.Id); - var sender = UserRepository.Get().First(x => x.Id == s.Id); - - Task.Run(async () => - { - try - { - message.CreatedAt = DateTime.UtcNow; - message.Sender = sender; - message.Recipient = recipient; - message.IsSupport = isSupport; - - SupportMessageRepository.Add(message); - - await MessageService.Emit($"support.{recipient.Id}.message", message); - - if (!recipient.SupportPending) - { - recipient.SupportPending = true; - UserRepository.Update(recipient); - - if (!message.IsSupport) - { - var systemMessage = new SupportMessage() - { - Recipient = recipient, - Sender = null, - IsSystem = true, - Message = "The support team has been notified. Please be patient" - }; - - SupportMessageRepository.Add(systemMessage); - - await MessageService.Emit($"support.{recipient.Id}.message", systemMessage); - } - - await MessageService.Emit($"support.new", recipient); - - Logger.Info("Support ticket created: " + recipient.Id); - //TODO: Ping or so - } - } - catch (Exception e) - { - Logger.Error("Error sending message"); - Logger.Error(e); - } - }); - } - - public async Task Close(User user) - { - var recipient = UserRepository.Get().First(x => x.Id == user.Id); - - recipient.SupportPending = false; - UserRepository.Update(recipient); - - var systemMessage = new SupportMessage() - { - Recipient = recipient, - Sender = null, - IsSystem = true, - Message = "The ticket is now closed. Type a message to open it again" - }; - - SupportMessageRepository.Add(systemMessage); - - await MessageService.Emit($"support.{recipient.Id}.message", systemMessage); - await MessageService.Emit($"support.close", recipient); - } - - public Task GetMessages(User r) - { - var recipient = UserRepository.Get().First(x => x.Id == r.Id); - - var messages = SupportMessageRepository - .Get() - .Include(x => x.Recipient) - .Include(x => x.Sender) - .Where(x => x.Recipient.Id == recipient.Id) - .AsEnumerable() - .TakeLast(50) - .OrderBy(x => x.Id) - .ToArray(); - - return Task.FromResult(messages); - } - - private Task Run() - { - ServiceScope = ServiceScopeFactory.CreateScope(); - - SupportMessageRepository = ServiceScope - .ServiceProvider - .GetRequiredService(); - - MessageService = ServiceScope - .ServiceProvider - .GetRequiredService(); - - UserRepository = ServiceScope - .ServiceProvider - .GetRequiredService(); - - return Task.CompletedTask; - } - - public void Dispose() - { - SupportMessageRepository.Dispose(); - UserRepository.Dispose(); - ServiceScope.Dispose(); - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs b/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs new file mode 100644 index 00000000..6369f34a --- /dev/null +++ b/Moonlight/App/Services/SupportChat/SupportChatAdminService.cs @@ -0,0 +1,134 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Events; +using Moonlight.App.Services.Sessions; + +namespace Moonlight.App.Services.SupportChat; + +public class SupportChatAdminService +{ + private readonly EventSystem Event; + private readonly IdentityService IdentityService; + private readonly SupportChatServerService ServerService; + + public Func? OnMessage { get; set; } + public Func? OnTypingChanged { get; set; } + + private User? User; + private User Recipient = null!; + private readonly List TypingUsers = new(); + + public SupportChatAdminService( + EventSystem eventSystem, + SupportChatServerService serverService, + IdentityService identityService) + { + Event = eventSystem; + ServerService = serverService; + IdentityService = identityService; + } + + public async Task Start(User recipient) + { + User = await IdentityService.Get(); + Recipient = recipient; + + if (User != null) + { + await Event.On($"supportChat.{Recipient.Id}.message", this, async message => + { + if (OnMessage != null) + { + if(message.Sender != null && message.Sender.Id == User.Id) + return; + + await OnMessage.Invoke(message); + } + }); + + await Event.On($"supportChat.{Recipient.Id}.typing", this, async user => + { + await HandleTyping(user); + }); + } + } + + public async Task GetMessages() + { + if (User == null) + return Array.Empty(); + + return await ServerService.GetMessages(Recipient); + } + + public async Task SendMessage(string content) + { + if (User != null) + { + return await ServerService.SendMessage(Recipient, content, User); + } + + return null!; + } + + private Task HandleTyping(User user) + { + lock (TypingUsers) + { + if (!TypingUsers.Contains(user)) + { + TypingUsers.Add(user); + + if (OnTypingChanged != null) + { + OnTypingChanged.Invoke( + TypingUsers + .Where(x => x.Id != User!.Id) + .Select(x => $"{x.FirstName} {x.LastName}") + .ToArray() + ); + } + + Task.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(5)); + + if (TypingUsers.Contains(user)) + { + TypingUsers.Remove(user); + + if (OnTypingChanged != null) + { + await OnTypingChanged.Invoke( + TypingUsers + .Where(x => x.Id != User!.Id) + .Select(x => $"{x.FirstName} {x.LastName}") + .ToArray() + ); + } + } + }); + } + } + + return Task.CompletedTask; + } + + public async Task SendTyping() + { + await Event.Emit($"supportChat.{Recipient.Id}.typing", User); + } + + public async Task Close() + { + await ServerService.CloseChat(Recipient); + } + + public async void Dispose() + { + if (User != null) + { + await Event.Off($"supportChat.{Recipient.Id}.message", this); + await Event.Off($"supportChat.{Recipient.Id}.typing", this); + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/SupportChat/SupportChatClientService.cs b/Moonlight/App/Services/SupportChat/SupportChatClientService.cs new file mode 100644 index 00000000..ad2b3eb7 --- /dev/null +++ b/Moonlight/App/Services/SupportChat/SupportChatClientService.cs @@ -0,0 +1,128 @@ +using Logging.Net; +using Moonlight.App.Database.Entities; +using Moonlight.App.Events; +using Moonlight.App.Services.Sessions; + +namespace Moonlight.App.Services.SupportChat; + +public class SupportChatClientService : IDisposable +{ + private readonly EventSystem Event; + private readonly IdentityService IdentityService; + private readonly SupportChatServerService ServerService; + + public Func? OnMessage { get; set; } + public Func? OnTypingChanged { get; set; } + + private User? User; + private readonly List TypingUsers = new(); + + public SupportChatClientService( + EventSystem eventSystem, + SupportChatServerService serverService, + IdentityService identityService) + { + Event = eventSystem; + ServerService = serverService; + IdentityService = identityService; + } + + public async Task Start() + { + User = await IdentityService.Get(); + + if (User != null) + { + await Event.On($"supportChat.{User.Id}.message", this, async message => + { + if (OnMessage != null) + { + if(message.Sender != null && message.Sender.Id == User.Id) + return; + + await OnMessage.Invoke(message); + } + }); + + await Event.On($"supportChat.{User.Id}.typing", this, async user => + { + await HandleTyping(user); + }); + } + } + + public async Task GetMessages() + { + if (User == null) + return Array.Empty(); + + return await ServerService.GetMessages(User); + } + + public async Task SendMessage(string content) + { + if (User != null) + { + return await ServerService.SendMessage(User, content, User); + } + + return null!; + } + + private Task HandleTyping(User user) + { + lock (TypingUsers) + { + if (!TypingUsers.Contains(user)) + { + TypingUsers.Add(user); + + if (OnTypingChanged != null) + { + OnTypingChanged.Invoke( + TypingUsers + .Where(x => x.Id != User!.Id) + .Select(x => $"{x.FirstName} {x.LastName}") + .ToArray() + ); + } + + Task.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(5)); + + if (TypingUsers.Contains(user)) + { + TypingUsers.Remove(user); + + if (OnTypingChanged != null) + { + await OnTypingChanged.Invoke( + TypingUsers + .Where(x => x.Id != User!.Id) + .Select(x => $"{x.FirstName} {x.LastName}") + .ToArray() + ); + } + } + }); + } + } + + return Task.CompletedTask; + } + + public async Task SendTyping() + { + await Event.Emit($"supportChat.{User!.Id}.typing", User); + } + + public async void Dispose() + { + if (User != null) + { + await Event.Off($"supportChat.{User.Id}.message", this); + await Event.Off($"supportChat.{User.Id}.typing", this); + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/SupportChat/SupportChatServerService.cs b/Moonlight/App/Services/SupportChat/SupportChatServerService.cs new file mode 100644 index 00000000..7fd0555c --- /dev/null +++ b/Moonlight/App/Services/SupportChat/SupportChatServerService.cs @@ -0,0 +1,146 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database.Entities; +using Moonlight.App.Events; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services.SupportChat; + +public class SupportChatServerService +{ + private readonly IServiceScopeFactory ServiceScopeFactory; + private readonly DateTimeService DateTimeService; + private readonly EventSystem Event; + + public SupportChatServerService( + IServiceScopeFactory serviceScopeFactory, + DateTimeService dateTimeService, + EventSystem eventSystem) + { + ServiceScopeFactory = serviceScopeFactory; + DateTimeService = dateTimeService; + Event = eventSystem; + } + + public Task GetMessages(User recipient) + { + using var scope = ServiceScopeFactory.CreateScope(); + var msgRepo = scope.ServiceProvider.GetRequiredService>(); + + var messages = msgRepo + .Get() + .Include(x => x.Recipient) + .Include(x => x.Sender) + .Where(x => x.Recipient.Id == recipient.Id) + .OrderByDescending(x => x.CreatedAt) + .AsEnumerable() + .Take(50) + .ToArray(); + + return Task.FromResult(messages); + } + + public async Task SendMessage(User recipient, string content, User? sender, string? attachment = null) + { + using var scope = ServiceScopeFactory.CreateScope(); + var msgRepo = scope.ServiceProvider.GetRequiredService>(); + var userRepo = scope.ServiceProvider.GetRequiredService>(); + + var message = new SupportChatMessage() + { + CreatedAt = DateTimeService.GetCurrent(), + IsQuestion = false, + Sender = sender == null ? null : userRepo.Get().First(x => x.Id == sender.Id), + Recipient = userRepo.Get().First(x => x.Id == recipient.Id), + Answer = "", + Attachment = attachment ?? "", + Content = content, + UpdatedAt = DateTimeService.GetCurrent() + }; + + var finalMessage = msgRepo.Add(message); + + await Event.Emit($"supportChat.{recipient.Id}.message", finalMessage); + await Event.Emit("supportChat.message", finalMessage); + + if (!userRepo.Get().First(x => x.Id == recipient.Id).SupportPending) + { + var ticketStart = new SupportChatMessage() + { + CreatedAt = DateTimeService.GetCurrent(), + IsQuestion = false, + Sender = null, + Recipient = userRepo.Get().First(x => x.Id == recipient.Id), + Answer = "", + Attachment = "", + Content = "Support ticket open", //TODO: Config + UpdatedAt = DateTimeService.GetCurrent() + }; + + var ticketStartFinal = msgRepo.Add(ticketStart); + + var user = userRepo.Get().First(x => x.Id == recipient.Id); + user.SupportPending = true; + userRepo.Update(user); + + await Event.Emit($"supportChat.{recipient.Id}.message", ticketStartFinal); + await Event.Emit("supportChat.message", ticketStartFinal); + await Event.Emit("supportChat.new", recipient); + } + + return finalMessage; + } + + public Task> GetOpenChats() + { + var result = new Dictionary(); + + using var scope = ServiceScopeFactory.CreateScope(); + var userRepo = scope.ServiceProvider.GetRequiredService>(); + var msgRepo = scope.ServiceProvider.GetRequiredService>(); + + foreach (var user in userRepo.Get().Where(x => x.SupportPending).ToArray()) + { + var lastMessage = msgRepo + .Get() + .Include(x => x.Recipient) + .Include(x => x.Sender) + .Where(x => x.Recipient.Id == user.Id) + .OrderByDescending(x => x.CreatedAt) + .AsEnumerable() + .FirstOrDefault(); + + result.Add(user, lastMessage); + } + + return Task.FromResult(result); + } + + public async Task CloseChat(User recipient) + { + using var scope = ServiceScopeFactory.CreateScope(); + var msgRepo = scope.ServiceProvider.GetRequiredService>(); + var userRepo = scope.ServiceProvider.GetRequiredService>(); + + var ticketEnd = new SupportChatMessage() + { + CreatedAt = DateTimeService.GetCurrent(), + IsQuestion = false, + Sender = null, + Recipient = userRepo.Get().First(x => x.Id == recipient.Id), + Answer = "", + Attachment = "", + Content = "Support ticket closed", //TODO: Config + UpdatedAt = DateTimeService.GetCurrent() + }; + + var ticketEndFinal = msgRepo.Add(ticketEnd); + + var user = userRepo.Get().First(x => x.Id == recipient.Id); + user.SupportPending = false; + userRepo.Update(user); + + await Event.Emit($"supportChat.{recipient.Id}.message", ticketEndFinal); + await Event.Emit("supportChat.message", ticketEndFinal); + await Event.Emit("supportChat.close", recipient); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/UserService.cs b/Moonlight/App/Services/UserService.cs index 6a6fe022..81633b14 100644 --- a/Moonlight/App/Services/UserService.cs +++ b/Moonlight/App/Services/UserService.cs @@ -19,6 +19,7 @@ public class UserService private readonly MailService MailService; private readonly IdentityService IdentityService; private readonly IpLocateService IpLocateService; + private readonly DateTimeService DateTimeService; private readonly string JwtSecret; @@ -29,7 +30,9 @@ public class UserService SecurityLogService securityLogService, AuditLogService auditLogService, MailService mailService, - IdentityService identityService, IpLocateService ipLocateService) + IdentityService identityService, + IpLocateService ipLocateService, + DateTimeService dateTimeService) { UserRepository = userRepository; TotpService = totpService; @@ -38,6 +41,7 @@ public class UserService MailService = mailService; IdentityService = identityService; IpLocateService = ipLocateService; + DateTimeService = dateTimeService; JwtSecret = configService .GetSection("Moonlight") @@ -70,12 +74,12 @@ public class UserService LastName = lastname, State = "", Status = UserStatus.Unverified, - CreatedAt = DateTime.UtcNow, + CreatedAt = DateTimeService.GetCurrent(), DiscordId = 0, TotpEnabled = false, TotpSecret = "", - UpdatedAt = DateTime.UtcNow, - TokenValidTime = DateTime.Now.AddDays(-5) + UpdatedAt = DateTimeService.GetCurrent(), + TokenValidTime = DateTimeService.GetCurrent().AddDays(-5) }); await MailService.SendMail(user!, "register", values => {}); @@ -168,7 +172,7 @@ public class UserService public async Task ChangePassword(User user, string password, bool isSystemAction = false) { user.Password = BCrypt.Net.BCrypt.HashPassword(password); - user.TokenValidTime = DateTime.Now; + user.TokenValidTime = DateTimeService.GetCurrent(); UserRepository.Update(user); if (isSystemAction) @@ -244,8 +248,8 @@ public class UserService var token = JwtBuilder.Create() .WithAlgorithm(new HMACSHA256Algorithm()) .WithSecret(JwtSecret) - .AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds()) - .AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()) + .AddClaim("exp", new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10)).ToUnixTimeSeconds()) + .AddClaim("iat", DateTimeService.GetCurrentUnixSeconds()) .AddClaim("userid", user.Id) .Encode(); diff --git a/Moonlight/App/Services/WebSpaceService.cs b/Moonlight/App/Services/WebSpaceService.cs new file mode 100644 index 00000000..91a3d718 --- /dev/null +++ b/Moonlight/App/Services/WebSpaceService.cs @@ -0,0 +1,191 @@ +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 Repository DatabaseRepository; + + private readonly CloudPanelApiHelper CloudPanelApiHelper; + + public WebSpaceService(Repository cloudPanelRepository, Repository webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper, Repository databaseRepository) + { + CloudPanelRepository = cloudPanelRepository; + WebSpaceRepository = webSpaceRepository; + CloudPanelApiHelper = cloudPanelApiHelper; + DatabaseRepository = databaseRepository; + } + + 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 + { + await CloudPanelApiHelper.Post(cloudPanel, "", null); + + return true; + } + catch (CloudPanelException e) + { + if (e.StatusCode == 404) + return true; + } + catch (Exception) + { + // ignored + } + + return false; + } + + public async Task IsHostUp(WebSpace w) + { + var webSpace = EnsureData(w); + + return await IsHostUp(webSpace.CloudPanel); + } + + public async Task IssueSslCertificate(WebSpace w) + { + var webspace = EnsureData(w); + + await CloudPanelApiHelper.Post(webspace.CloudPanel, "letsencrypt/install/certificate", new InstallLetsEncrypt() + { + DomainName = webspace.Domain + }); + } + + #region Databases + + public Task GetDatabases(WebSpace w) + { + return Task.FromResult(WebSpaceRepository + .Get() + .Include(x => x.Databases) + .First(x => x.Id == w.Id) + .Databases.ToArray()); + } + + public async Task CreateDatabase(WebSpace w, string name, string password) + { + if (DatabaseRepository.Get().Any(x => x.UserName == name)) + throw new DisplayException("A database with this name does already exist"); + + var webspace = EnsureData(w); + + var database = new MySqlDatabase() + { + UserName = name, + Password = password + }; + + await CloudPanelApiHelper.Post(webspace.CloudPanel, "db", new AddDatabase() + { + DomainName = webspace.Domain, + DatabaseName = database.UserName, + DatabaseUserName = database.UserName, + DatabaseUserPassword = database.Password + }); + + webspace.Databases.Add(database); + WebSpaceRepository.Update(webspace); + } + + public async Task DeleteDatabase(WebSpace w, MySqlDatabase database) + { + var webspace = EnsureData(w); + + await CloudPanelApiHelper.Delete(webspace.CloudPanel, $"db/{database.UserName}", null); + + webspace.Databases.Remove(database); + WebSpaceRepository.Update(webspace); + } + + #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 22aba371..a0d9713d 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -19,10 +19,12 @@ + + @@ -41,6 +43,7 @@ + @@ -67,6 +70,7 @@ + diff --git a/Moonlight/Pages/_Host.cshtml b/Moonlight/Pages/_Host.cshtml index 7f5640de..1eb715d7 100644 --- a/Moonlight/Pages/_Host.cshtml +++ b/Moonlight/Pages/_Host.cshtml @@ -1,29 +1,54 @@ @page "/" +@using Moonlight.App.Services @namespace Moonlight.Pages + +@inject SmartTranslateService SmartTranslateService + @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ Layout = "_Layout"; } - +
-

-
- Connecting to moonlight servers -

+
-

-
- Connection to moonlight servers failed -

+
-

-
- Connection to moonlight servers rejected -

+
-
+ \ No newline at end of file diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index 7a5518e6..ca9c2287 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 @@ -51,7 +54,7 @@ - + Loading Logo + @{ + var loadingMessage = LoadingMessageRepository.Get().Random(); + } +
- CHANGEME + @(loadingMessage.Message)
@@ -93,25 +100,18 @@ - - - - - - - - - - - - + + + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 76e01893..7621c7bf 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -2,7 +2,9 @@ using BlazorDownloadFile; using BlazorTable; using CurrieTechnologies.Razor.SweetAlert2; using Logging.Net; +using Moonlight.App.ApiClients.CloudPanel; using Moonlight.App.Database; +using Moonlight.App.Events; using Moonlight.App.Helpers; using Moonlight.App.LogMigrator; using Moonlight.App.Repositories; @@ -17,7 +19,7 @@ using Moonlight.App.Services.Notifications; using Moonlight.App.Services.OAuth2; using Moonlight.App.Services.Sessions; using Moonlight.App.Services.Statistics; -using Moonlight.App.Services.Support; +using Moonlight.App.Services.SupportChat; namespace Moonlight { @@ -41,7 +43,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 @@ -54,23 +62,20 @@ 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(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(typeof(Repository<>)); // Services builder.Services.AddSingleton(); @@ -85,7 +90,6 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); @@ -97,8 +101,13 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -106,8 +115,6 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddSingleton(); - // Loggers builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -116,10 +123,10 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddSingleton(); - // Support - builder.Services.AddSingleton(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + // Support chat + builder.Services.AddSingleton(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); // Helpers builder.Services.AddSingleton(); @@ -130,11 +137,13 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // Third party services builder.Services.AddBlazorTable(); @@ -160,14 +169,12 @@ namespace Moonlight app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); - - // Support service - var supportServerService = app.Services.GetRequiredService(); - + // AutoStart services _ = app.Services.GetRequiredService(); _ = app.Services.GetRequiredService(); _ = app.Services.GetRequiredService(); + _ = app.Services.GetRequiredService(); // Discord bot service //var discordBotService = app.Services.GetRequiredService(); diff --git a/Moonlight/Properties/launchSettings.json b/Moonlight/Properties/launchSettings.json index 778ebef4..49e61cf7 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/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index 3d59dd7c..bff6a292 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -2,6 +2,7 @@ @using Moonlight.App.Exceptions @using Moonlight.App.Services @using Logging.Net +@using Moonlight.App.ApiClients.CloudPanel @inherits ErrorBoundaryBase @inject AlertService AlertService @@ -57,12 +58,16 @@ 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) + else if (exception is CloudPanelException cloudPanelException) { await AlertService.Error( - SmartTranslateService.Translate("Error from plesk"), - pleskException.Message + SmartTranslateService.Translate("Error from cloud panel"), + cloudPanelException.Message ); } else if (exception is NotImplementedException) 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/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/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/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/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 diff --git a/Moonlight/Shared/Components/Partials/Navbar.razor b/Moonlight/Shared/Components/Partials/Navbar.razor index f0f01e56..79fe3f9d 100644 --- a/Moonlight/Shared/Components/Partials/Navbar.razor +++ b/Moonlight/Shared/Components/Partials/Navbar.razor @@ -9,33 +9,36 @@ @inject NavigationManager NavigationManager @inject CookieService CookieService -