From 5085befb225a2f317c53080dc2a190283514b989 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:23:14 +0200 Subject: [PATCH 01/45] ftp file maneger --- Moonlight/App/Helpers/Files/FtpFileAccess.cs | 184 +++++++++++++++++++ Moonlight/Moonlight.csproj | 1 + Moonlight/Shared/Views/Test.razor | 7 +- Moonlight/resources/lang/de_de.lang | 2 + 4 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 Moonlight/App/Helpers/Files/FtpFileAccess.cs diff --git a/Moonlight/App/Helpers/Files/FtpFileAccess.cs b/Moonlight/App/Helpers/Files/FtpFileAccess.cs new file mode 100644 index 00000000..ec3e66c2 --- /dev/null +++ b/Moonlight/App/Helpers/Files/FtpFileAccess.cs @@ -0,0 +1,184 @@ +using System.Net; +using System.Text; +using FluentFTP; + +namespace Moonlight.App.Helpers.Files; + +public class FtpFileAccess : FileAccess +{ + private string FtpHost, FtpUser, FtpPassword; + private int FtpPort; + + private AsyncFtpClient Client; + + public FtpFileAccess(string ftpHost, int ftpPort, string ftpUser, string ftpPassword) + { + FtpHost = ftpHost; + FtpPort = ftpPort; + FtpUser = ftpUser; + FtpPassword = ftpPassword; + + Client = new AsyncFtpClient(FtpHost, FtpUser, FtpPassword, FtpPort); + } + + private async Task EnsureConnect() + { + if (!Client.IsConnected) + await Client.AutoConnect(); + } + + public override async Task Ls() + { + await EnsureConnect(); + + var x = new List(); + + foreach (FtpListItem item in await Client.GetListing(CurrentPath)) + { + long size = 0; + + if (item.Type == FtpObjectType.File) + { + size = await Client.GetFileSize(item.FullName); + } + + x.Add(new() + { + Name = item.Name, + Size = size, + IsFile = item.Type == FtpObjectType.File, + }); + } + + return 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 async Task Read(FileData fileData) + { + await EnsureConnect(); + + var s = new MemoryStream(); + await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + var data = s.ToArray(); + s.Dispose(); + var str = Encoding.UTF8.GetString(data); + return str; + } + + public override async Task Write(FileData fileData, string content) + { + await EnsureConnect(); + + var s = new MemoryStream(); + s.Write(Encoding.UTF8.GetBytes(content)); + s.Position = 0; + await Client.UploadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name, FtpRemoteExists.Overwrite); + s.Dispose(); + } + + public override async Task Upload(string name, Stream dataStream, Action? progressUpdated = null) + { + await EnsureConnect(); + + IProgress progress = new Progress(x => + { + progressUpdated((int) x.Progress); + }); + await Client.UploadStream(dataStream, CurrentPath.TrimEnd('/') + "/" + name, FtpRemoteExists.Overwrite, false, progress); + dataStream.Dispose(); + } + + public override async Task MkDir(string name) + { + await EnsureConnect(); + + await Client.CreateDirectory(CurrentPath.TrimEnd('/') + "/" + name + "/"); + } + + public override Task Pwd() + { + return Task.FromResult(CurrentPath); + } + + public override async Task DownloadUrl(FileData fileData) + { + await EnsureConnect(); + + throw new NotImplementedException(); + } + + public override async Task DownloadStream(FileData fileData) + { + await EnsureConnect(); + + var s = new MemoryStream(); + await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + return s; + } + + public override async Task Delete(FileData fileData) + { + await EnsureConnect(); + + if (fileData.IsFile) + await Client.DeleteFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name); + else + await Client.DeleteDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name); + } + + public override async Task Move(FileData fileData, string newPath) + { + await EnsureConnect(); + + if (fileData.IsFile) + await Client.MoveFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath); + else + await Client.MoveDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath); + } + + public override async Task Compress(params FileData[] files) + { + await EnsureConnect(); + + throw new NotImplementedException(); + } + + public override async Task Decompress(FileData fileData) + { + await EnsureConnect(); + + throw new NotImplementedException(); + } + + public override Task GetLaunchUrl() + { + return Task.FromResult( + $"ftp://{FtpUser}:{FtpPassword}@{FtpHost}:{FtpPort}/"); + } + + public override object Clone() + { + return new FtpFileAccess(FtpHost, FtpPort, FtpUser, FtpPassword); + } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 9609e642..2c6662f8 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -20,6 +20,7 @@ + diff --git a/Moonlight/Shared/Views/Test.razor b/Moonlight/Shared/Views/Test.razor index eafabd5f..db4a2862 100644 --- a/Moonlight/Shared/Views/Test.razor +++ b/Moonlight/Shared/Views/Test.razor @@ -27,12 +27,7 @@ private Task Load(LazyLoader arg) { - var server = ServerRepository - .Get() - .Include(x => x.Node) - .First(); - - FileAccess = new WingsFileAccess(WingsApiHelper, WingsJwtHelper, server, ConfigService, User); + FileAccess = new FtpFileAccess("vps01.so.host.endelon.link", 21, "example.com", "61P8JZzfjSNyhtZl"); return Task.CompletedTask; } diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 429e88a1..3c60d0df 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -482,3 +482,5 @@ Node offline;Node offline The node the server is running on is currently offline;The node the server is running on is currently offline Server not found;Server not found A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server +Compress;Compress +Decompress;Decompress From 35e1f99277054d1aec77f942bccdbacdc581b79d Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:43:35 +0200 Subject: [PATCH 02/45] ordered file list --- Moonlight/App/Helpers/Files/FtpFileAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moonlight/App/Helpers/Files/FtpFileAccess.cs b/Moonlight/App/Helpers/Files/FtpFileAccess.cs index ec3e66c2..0b6a9de4 100644 --- a/Moonlight/App/Helpers/Files/FtpFileAccess.cs +++ b/Moonlight/App/Helpers/Files/FtpFileAccess.cs @@ -33,7 +33,7 @@ public class FtpFileAccess : FileAccess var x = new List(); - foreach (FtpListItem item in await Client.GetListing(CurrentPath)) + foreach (FtpListItem item in (await Client.GetListing(CurrentPath)).OrderBy(x => x.Type + " " + x.Name)) { long size = 0; From dbaaf6a32e13505e129d7083c9737a9813af5030 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:55:56 +0200 Subject: [PATCH 03/45] fixed file manager design --- .../Shared/Components/FileManagerPartials/FileManager.razor | 6 +++--- .../Shared/Components/FileManagerPartials/FileView.razor | 2 +- Moonlight/resources/lang/de_de.lang | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor index f1f9eea1..2fe4c06b 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor @@ -24,7 +24,7 @@ else {
-
+
@@ -59,7 +59,7 @@ else else {
-
+
@(Formatter.FormatSize(file.Size))
-
+
@if (ContextActions.Any()) { diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 3c60d0df..d71bd8e3 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -484,3 +484,6 @@ Server not found;Server not found A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server Compress;Compress Decompress;Decompress +Moving;Moving +Compressing;Compressing +selected;selected From 46f544b5f83866674f52b53b158cd7fa0a065a88 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 00:14:15 +0200 Subject: [PATCH 04/45] Added website dash, files, ftp, databases, ssl. added plesk online check, did some renamming and stuff . Fixed some things --- Moonlight/App/Database/DataContext.cs | 2 + .../App/Database/Entities/PleskServer.cs | 9 + Moonlight/App/Database/Entities/Website.cs | 12 + ...522_AddedPleskAndWebsiteModels.Designer.cs | 946 +++++++++++++++++ ...230404181522_AddedPleskAndWebsiteModels.cs | 84 ++ ...0405162507_UpdatedWebsiteModel.Designer.cs | 954 ++++++++++++++++++ .../20230405162507_UpdatedWebsiteModel.cs | 40 + .../Migrations/DataContextModelSnapshot.cs | 78 ++ Moonlight/App/Exceptions/DaemonException.cs | 32 + Moonlight/App/Exceptions/PleskException.cs | 32 + Moonlight/App/Helpers/DaemonApiHelper.cs | 2 +- Moonlight/App/Helpers/PleskApiHelper.cs | 220 ++++ .../App/Models/Forms/PleskServerDataModel.cs | 16 + .../App/Models/Forms/WebsiteAdminDataModel.cs | 14 + .../App/Models/Forms/WebsiteDataModel.cs | 10 + .../App/Models/Plesk/Requests/CliCall.cs | 10 + .../App/Models/Plesk/Requests/CreateDomain.cs | 45 + .../App/Models/Plesk/Resources/CliResult.cs | 13 + .../App/Models/Plesk/Resources/Client.cs | 45 + .../Models/Plesk/Resources/CreateResult.cs | 12 + .../Models/Plesk/Resources/ServerStatus.cs | 18 + .../App/Repositories/PleskServerRepository.cs | 44 + .../App/Repositories/WebsiteRepository.cs | 44 + Moonlight/App/Services/NodeService.cs | 18 + Moonlight/App/Services/ServerService.cs | 18 +- Moonlight/App/Services/WebsiteService.cs | 236 +++++ Moonlight/Program.cs | 4 + .../ErrorBoundaries/SoftErrorBoundary.razor | 7 + .../Components/Forms/DeleteButton.razor | 7 +- .../Navigations/AdminWebsitesNavigation.razor | 22 + .../Components/Partials/SidebarMenu.razor | 29 +- .../WebsiteControl/WebsiteDashboard.razor | 101 ++ .../WebsiteControl/WebsiteFiles.razor | 24 + .../WebsiteControl/WebsiteFtp.razor | 64 ++ .../WebsiteControl/WebsiteNavigation.razor | 57 ++ .../Shared/Views/Admin/Servers/New.razor | 2 - .../Shared/Views/Admin/Websites/Index.razor | 93 ++ .../Shared/Views/Admin/Websites/New.razor | 79 ++ .../Views/Admin/Websites/Servers/Edit.razor | 98 ++ .../Views/Admin/Websites/Servers/Index.razor | 115 +++ .../Views/Admin/Websites/Servers/New.razor | 55 + Moonlight/Shared/Views/Server/Index.razor | 24 +- Moonlight/Shared/Views/Website/Index.razor | 129 +++ Moonlight/Shared/Views/Websites/Index.razor | 1 + Moonlight/Shared/Views/Websites/New.razor | 1 + Moonlight/resources/lang/de_de.lang | 13 + 46 files changed, 3825 insertions(+), 54 deletions(-) create mode 100644 Moonlight/App/Database/Entities/PleskServer.cs create mode 100644 Moonlight/App/Database/Entities/Website.cs create mode 100644 Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs create mode 100644 Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs create mode 100644 Moonlight/App/Exceptions/DaemonException.cs create mode 100644 Moonlight/App/Exceptions/PleskException.cs create mode 100644 Moonlight/App/Helpers/PleskApiHelper.cs create mode 100644 Moonlight/App/Models/Forms/PleskServerDataModel.cs create mode 100644 Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs create mode 100644 Moonlight/App/Models/Forms/WebsiteDataModel.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CliCall.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CreateDomain.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/CliResult.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/Client.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/CreateResult.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/ServerStatus.cs create mode 100644 Moonlight/App/Repositories/PleskServerRepository.cs create mode 100644 Moonlight/App/Repositories/WebsiteRepository.cs create mode 100644 Moonlight/App/Services/WebsiteService.cs create mode 100644 Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteFiles.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteFtp.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/New.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/New.razor create mode 100644 Moonlight/Shared/Views/Website/Index.razor create mode 100644 Moonlight/Shared/Views/Websites/Index.razor create mode 100644 Moonlight/Shared/Views/Websites/New.razor diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 906ce00c..58bd3db4 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -39,6 +39,8 @@ 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; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/PleskServer.cs b/Moonlight/App/Database/Entities/PleskServer.cs new file mode 100644 index 00000000..a4cd6307 --- /dev/null +++ b/Moonlight/App/Database/Entities/PleskServer.cs @@ -0,0 +1,9 @@ +namespace Moonlight.App.Database.Entities; + +public class PleskServer +{ + public int Id { get; set; } + public string Name { get; set; } = ""; + public string ApiUrl { get; set; } = ""; + public string ApiKey { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/Website.cs b/Moonlight/App/Database/Entities/Website.cs new file mode 100644 index 00000000..56e9fd12 --- /dev/null +++ b/Moonlight/App/Database/Entities/Website.cs @@ -0,0 +1,12 @@ +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/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs new file mode 100644 index 00000000..c8355767 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs @@ -0,0 +1,946 @@ +// +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("20230404181522_AddedPleskAndWebsiteModels")] + partial class AddedPleskAndWebsiteModels + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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.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"); + + 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.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .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.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") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs new file mode 100644 index 00000000..4c8834fa --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs @@ -0,0 +1,84 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedPleskAndWebsiteModels : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PleskServers", + 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_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), + BaseDomain = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PleskId = table.Column(type: "int", nullable: false), + PleskServerId = table.Column(type: "int", nullable: false), + OwnerId = 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"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Websites"); + + migrationBuilder.DropTable( + name: "PleskServers"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs new file mode 100644 index 00000000..5d2125bd --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs @@ -0,0 +1,954 @@ +// +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("20230405162507_UpdatedWebsiteModel")] + partial class UpdatedWebsiteModel + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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.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"); + + 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.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.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") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs new file mode 100644 index 00000000..6085df04 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class UpdatedWebsiteModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FtpLogin", + table: "Websites", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "FtpPassword", + table: "Websites", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FtpLogin", + table: "Websites"); + + migrationBuilder.DropColumn( + name: "FtpPassword", + table: "Websites"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 65062e19..14331192 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -395,6 +395,29 @@ 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") @@ -697,6 +720,42 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); + 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") @@ -847,6 +906,25 @@ namespace Moonlight.App.Database.Migrations b.Navigation("CurrentSubscription"); }); + 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"); diff --git a/Moonlight/App/Exceptions/DaemonException.cs b/Moonlight/App/Exceptions/DaemonException.cs new file mode 100644 index 00000000..05070d4a --- /dev/null +++ b/Moonlight/App/Exceptions/DaemonException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Moonlight.App.Exceptions; + +[Serializable] +public class DaemonException : Exception +{ + public int StatusCode { private get; set; } + + public DaemonException() + { + } + + public DaemonException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public DaemonException(string message) : base(message) + { + } + + public DaemonException(string message, Exception inner) : base(message, inner) + { + } + + protected DaemonException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/Exceptions/PleskException.cs b/Moonlight/App/Exceptions/PleskException.cs new file mode 100644 index 00000000..e29676f0 --- /dev/null +++ b/Moonlight/App/Exceptions/PleskException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Moonlight.App.Exceptions; + +[Serializable] +public class PleskException : Exception +{ + public int StatusCode { private get; set; } + + public PleskException() + { + } + + public PleskException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public PleskException(string message) : base(message) + { + } + + public PleskException(string message, Exception inner) : base(message, inner) + { + } + + protected PleskException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/DaemonApiHelper.cs b/Moonlight/App/Helpers/DaemonApiHelper.cs index 45d91a29..4c776a44 100644 --- a/Moonlight/App/Helpers/DaemonApiHelper.cs +++ b/Moonlight/App/Helpers/DaemonApiHelper.cs @@ -37,7 +37,7 @@ public class DaemonApiHelper { if (response.StatusCode != 0) { - throw new WingsException( + throw new DaemonException( $"An error occured: ({response.StatusCode}) {response.Content}", (int)response.StatusCode ); diff --git a/Moonlight/App/Helpers/PleskApiHelper.cs b/Moonlight/App/Helpers/PleskApiHelper.cs new file mode 100644 index 00000000..87ea9878 --- /dev/null +++ b/Moonlight/App/Helpers/PleskApiHelper.cs @@ -0,0 +1,220 @@ +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/Models/Forms/PleskServerDataModel.cs b/Moonlight/App/Models/Forms/PleskServerDataModel.cs new file mode 100644 index 00000000..78e4920a --- /dev/null +++ b/Moonlight/App/Models/Forms/PleskServerDataModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class PleskServerDataModel +{ + [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 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/WebsiteAdminDataModel.cs b/Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs new file mode 100644 index 00000000..f3884d06 --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteAdminDataModel +{ + [Required(ErrorMessage = "You need a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a owner")] + public User User { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/WebsiteDataModel.cs b/Moonlight/App/Models/Forms/WebsiteDataModel.cs new file mode 100644 index 00000000..d4a7f4bf --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteDataModel +{ + [Required(ErrorMessage = "You need a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CliCall.cs b/Moonlight/App/Models/Plesk/Requests/CliCall.cs new file mode 100644 index 00000000..c6fb06ee --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CliCall.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CliCall +{ + [JsonProperty("params")] public List Params { get; set; } = new(); + + [JsonProperty("env")] public Dictionary Env { get; set; } = new(); +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs b/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs new file mode 100644 index 00000000..1ef11caa --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDomain +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("hosting_type")] + public string HostingType { get; set; } + + [JsonProperty("hosting_settings")] + public HostingSettingsModel HostingSettings { get; set; } + + [JsonProperty("owner_client")] + public OwnerClientModel OwnerClient { get; set; } + + [JsonProperty("plan")] + public PlanModel Plan { get; set; } + + public partial class HostingSettingsModel + { + [JsonProperty("ftp_login")] + public string FtpLogin { get; set; } + + [JsonProperty("ftp_password")] + public string FtpPassword { get; set; } + } + + public partial class OwnerClientModel + { + [JsonProperty("id")] + public long Id { get; set; } + } + + public partial class PlanModel + { + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/CliResult.cs b/Moonlight/App/Models/Plesk/Resources/CliResult.cs new file mode 100644 index 00000000..255ff8d4 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/CliResult.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class CliResult +{ + [JsonProperty("code")] + public int Code { get; set; } + + [JsonProperty("stdout")] public string Stdout { get; set; } = ""; + + [JsonProperty("stderr")] public string Stderr { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/Client.cs b/Moonlight/App/Models/Plesk/Resources/Client.cs new file mode 100644 index 00000000..8bf96ae2 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/Client.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class Client +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("created")] + public DateTimeOffset Created { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("company")] + public string Company { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("status")] + public long Status { get; set; } + + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("locale")] + public string Locale { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } + + [JsonProperty("owner_login")] + public string OwnerLogin { get; set; } + + [JsonProperty("external_id")] + public string ExternalId { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/CreateResult.cs b/Moonlight/App/Models/Plesk/Resources/CreateResult.cs new file mode 100644 index 00000000..54415aed --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/CreateResult.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class CreateResult +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs b/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs new file mode 100644 index 00000000..32962dbe --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class ServerStatus +{ + [JsonProperty("platform")] + public string Platform { get; set; } + + [JsonProperty("hostname")] + public string Hostname { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } + + [JsonProperty("panel_version")] + public string PanelVersion { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/PleskServerRepository.cs b/Moonlight/App/Repositories/PleskServerRepository.cs new file mode 100644 index 00000000..53742635 --- /dev/null +++ b/Moonlight/App/Repositories/PleskServerRepository.cs @@ -0,0 +1,44 @@ +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/WebsiteRepository.cs b/Moonlight/App/Repositories/WebsiteRepository.cs new file mode 100644 index 00000000..4a45b5da --- /dev/null +++ b/Moonlight/App/Repositories/WebsiteRepository.cs @@ -0,0 +1,44 @@ +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/NodeService.cs b/Moonlight/App/Services/NodeService.cs index 577214ef..87f0c307 100644 --- a/Moonlight/App/Services/NodeService.cs +++ b/Moonlight/App/Services/NodeService.cs @@ -41,4 +41,22 @@ public class NodeService { return await DaemonApiHelper.Get(node, "stats/container"); } + + public async Task IsHostUp(Node node) + { + try + { + //TODO: Implement status caching + var data = await GetStatus(node); + + if (data != null) + return true; + } + catch (Exception) + { + // ignored + } + + return false; + } } \ No newline at end of file diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 8ec47c9f..bc22343d 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -29,6 +29,7 @@ public class ServerService private readonly SecurityLogService SecurityLogService; private readonly AuditLogService AuditLogService; private readonly ErrorLogService ErrorLogService; + private readonly NodeService NodeService; public ServerService( ServerRepository serverRepository, @@ -42,7 +43,8 @@ public class ServerService WingsJwtHelper wingsJwtHelper, SecurityLogService securityLogService, AuditLogService auditLogService, - ErrorLogService errorLogService) + ErrorLogService errorLogService, + NodeService nodeService) { ServerRepository = serverRepository; WingsApiHelper = wingsApiHelper; @@ -56,6 +58,7 @@ public class ServerService SecurityLogService = securityLogService; AuditLogService = auditLogService; ErrorLogService = errorLogService; + NodeService = nodeService; } private Server EnsureNodeData(Server s) @@ -252,16 +255,14 @@ public class ServerService Node node; if (n == null) - { - node = NodeRepository.Get().Include(x => x.Allocations).First(); //TODO: Smart deploy - } - else { node = NodeRepository .Get() .Include(x => x.Allocations) .First(x => x.Id == n.Id); } + else + node = n; NodeAllocation freeAllo; @@ -395,4 +396,11 @@ public class ServerService ServerRepository.Delete(s); } + + public async Task IsHostUp(Server s) + { + var server = EnsureNodeData(s); + + return await NodeService.IsHostUp(server.Node); + } } \ No newline at end of file diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs new file mode 100644 index 00000000..44b2994e --- /dev/null +++ b/Moonlight/App/Services/WebsiteService.cs @@ -0,0 +1,236 @@ +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; + + public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository) + { + WebsiteRepository = websiteRepository; + PleskApiHelper = pleskApiHelper; + PleskServerRepository = pleskServerRepository; + } + + 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; + } + + public async Task GetHost(PleskServer pleskServer) + { + return (await PleskApiHelper.Get(pleskServer, "server")).Hostname; + } + + 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; + } + + public async Task GetSslCertificates(Website w) + { + var website = EnsureData(w); + var certs = new List(); + + Logger.Debug("1"); + + var data = await ExecuteCli(website.PleskServer, "certificate", p => + { + p.Add("-l"); + p.Add("-domain"); + p.Add(w.BaseDomain); + }); + + Logger.Debug("2"); + + 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); + + foreach (var part in parts) + { + Logger.Debug(part); + } + + 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() + { + + } + + 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) + return WebsiteRepository + .Get() + .Include(x => x.PleskServer) + .First(x => x.Id == website.Id); + + return website; + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 5b4efebf..832b1f1e 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -60,6 +60,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -89,6 +91,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -120,6 +123,7 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index ca47b591..cbad558c 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -36,6 +36,13 @@ wingsException.Message ); } + else if (exception is PleskException pleskException) + { + await AlertService.Error( + SmartTranslateService.Translate("Error from plesk"), + pleskException.Message + ); + } else { throw exception; diff --git a/Moonlight/Shared/Components/Forms/DeleteButton.razor b/Moonlight/Shared/Components/Forms/DeleteButton.razor index 4c37ec9f..7cd719a2 100644 --- a/Moonlight/Shared/Components/Forms/DeleteButton.razor +++ b/Moonlight/Shared/Components/Forms/DeleteButton.razor @@ -35,12 +35,7 @@ else { if (Confirm) { - var b = await AlertService.YesNo( - SmartTranslateService.Translate("Are you sure?"), - SmartTranslateService.Translate("Do you really want to delete it?"), - SmartTranslateService.Translate("Yes"), - SmartTranslateService.Translate("No") - ); + var b = await AlertService.ConfirmMath(); if (b) { diff --git a/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor new file mode 100644 index 00000000..40b98026 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor @@ -0,0 +1,22 @@ + + +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index 37dd7d6d..2d82030e 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -155,32 +155,13 @@ else
- -
+

-

-
+
@@ -129,8 +129,8 @@
-
-
+
+
-
+
Date: Thu, 6 Apr 2023 02:20:05 +0200 Subject: [PATCH 07/45] Implemented ssl options for websites --- Moonlight/App/Services/WebsiteService.cs | 79 +++++++++++++++---- .../WebsiteControl/WebsiteDashboard.razor | 28 ++++--- .../WebsiteControl/WebsiteNavigation.razor | 2 +- Moonlight/Shared/Views/Website/Index.razor | 2 + Moonlight/resources/lang/de_de.lang | 6 ++ 5 files changed, 93 insertions(+), 24 deletions(-) diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs index 44b2994e..ad85d488 100644 --- a/Moonlight/App/Services/WebsiteService.cs +++ b/Moonlight/App/Services/WebsiteService.cs @@ -16,12 +16,14 @@ 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) + 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) @@ -149,8 +151,6 @@ public class WebsiteService { var website = EnsureData(w); var certs = new List(); - - Logger.Debug("1"); var data = await ExecuteCli(website.PleskServer, "certificate", p => { @@ -158,9 +158,7 @@ public class WebsiteService p.Add("-domain"); p.Add(w.BaseDomain); }); - - Logger.Debug("2"); - + string[] lines = data.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) @@ -169,11 +167,6 @@ public class WebsiteService { string[] parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var part in parts) - { - Logger.Debug(part); - } - if(parts.Length > 6) certs.Add($"{parts[4]} {parts[5]} {parts[6]}"); } @@ -187,9 +180,66 @@ public class WebsiteService return certs.ToArray(); } - public async Task CreateSslCertificate() + 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"); + } } public async Task CreateFileAccess(Website w) @@ -225,10 +275,11 @@ public class WebsiteService private Website EnsureData(Website website) { - if (website.PleskServer == null) + 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; diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor b/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor index 54cef8ad..d499e873 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor @@ -15,11 +15,20 @@
- +
- SSL certificates + + SSL certificates + +
+ + +
@if (Certs.Any()) @@ -47,11 +56,6 @@ @(cert) - - - No SSL certificate found + No SSL certificates found
}
@@ -85,17 +89,23 @@ private string[] Certs; + private LazyLoader LazyLoader; + private async Task Load(LazyLoader lazyLoader) { await lazyLoader.SetText("Loading certificates"); Certs = await WebsiteService.GetSslCertificates(CurrentWebsite); } - private async Task UseCertificate(string name) + private async Task CreateCertificate() { + await WebsiteService.CreateSslCertificate(CurrentWebsite); + await LazyLoader.Reload(); } private async Task DeleteCertificate(string name) { + await WebsiteService.DeleteSslCertificate(CurrentWebsite, name); + await LazyLoader.Reload(); } } \ No newline at end of file diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor b/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor index a2a16d84..bec6068c 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor @@ -1,6 +1,6 @@ @using Moonlight.App.Database.Entities -
+
diff --git a/Moonlight/Shared/Views/Website/Index.razor b/Moonlight/Shared/Views/Website/Index.razor index 1cc614ad..cc21a6b8 100644 --- a/Moonlight/Shared/Views/Website/Index.razor +++ b/Moonlight/Shared/Views/Website/Index.razor @@ -4,9 +4,11 @@ @using Moonlight.App.Services @using Moonlight.Shared.Components.WebsiteControl @using Microsoft.EntityFrameworkCore +@using Moonlight.App.Services.Interop @inject WebsiteRepository WebsiteRepository @inject WebsiteService WebsiteService +@inject ToastService ToastService @if (CurrentWebsite == null) diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 3bc9b904..a4f49ceb 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -500,3 +500,9 @@ Ftp Password;Ftp Password Use;Use SSL Certificates;SSL Certificates SSL certificates;SSL certificates +Issue certificate;Issue certificate +New plesk server;New plesk server +Api url;Api url +Host system offline;Host system offline +The host system the website is running on is currently offline;The host system the website is running on is currently offline +No SSL certificates found;No SSL certificates found From 06c280704c0a1f96f1c3c45750a4e35a82400135 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 02:43:44 +0200 Subject: [PATCH 08/45] Fixed ftp download file. Prevented memory stream overload --- Moonlight/App/Helpers/Files/FtpFileAccess.cs | 32 +++++++++---------- .../FileManagerPartials/FileManager.razor | 4 +-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Moonlight/App/Helpers/Files/FtpFileAccess.cs b/Moonlight/App/Helpers/Files/FtpFileAccess.cs index 0b6a9de4..df17025d 100644 --- a/Moonlight/App/Helpers/Files/FtpFileAccess.cs +++ b/Moonlight/App/Helpers/Files/FtpFileAccess.cs @@ -1,6 +1,7 @@ using System.Net; using System.Text; using FluentFTP; +using Moonlight.App.Exceptions; namespace Moonlight.App.Helpers.Files; @@ -78,10 +79,10 @@ public class FtpFileAccess : FileAccess { await EnsureConnect(); - var s = new MemoryStream(); + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); var data = s.ToArray(); - s.Dispose(); + await s.DisposeAsync(); var str = Encoding.UTF8.GetString(data); return str; } @@ -90,11 +91,11 @@ public class FtpFileAccess : FileAccess { await EnsureConnect(); - var s = new MemoryStream(); + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option s.Write(Encoding.UTF8.GetBytes(content)); s.Position = 0; await Client.UploadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name, FtpRemoteExists.Overwrite); - s.Dispose(); + await s.DisposeAsync(); } public override async Task Upload(string name, Stream dataStream, Action? progressUpdated = null) @@ -103,10 +104,9 @@ public class FtpFileAccess : FileAccess IProgress progress = new Progress(x => { - progressUpdated((int) x.Progress); + progressUpdated?.Invoke((int)x.Progress); }); await Client.UploadStream(dataStream, CurrentPath.TrimEnd('/') + "/" + name, FtpRemoteExists.Overwrite, false, progress); - dataStream.Dispose(); } public override async Task MkDir(string name) @@ -121,10 +121,8 @@ public class FtpFileAccess : FileAccess return Task.FromResult(CurrentPath); } - public override async Task DownloadUrl(FileData fileData) + public override Task DownloadUrl(FileData fileData) { - await EnsureConnect(); - throw new NotImplementedException(); } @@ -132,8 +130,12 @@ public class FtpFileAccess : FileAccess { await EnsureConnect(); - var s = new MemoryStream(); - await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + var downloaded = await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + + if (!downloaded) + throw new DisplayException("Unable to download file"); + return s; } @@ -157,17 +159,13 @@ public class FtpFileAccess : FileAccess await Client.MoveDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath); } - public override async Task Compress(params FileData[] files) + public override Task Compress(params FileData[] files) { - await EnsureConnect(); - throw new NotImplementedException(); } - public override async Task Decompress(FileData fileData) + public override Task Decompress(FileData fileData) { - await EnsureConnect(); - throw new NotImplementedException(); } diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor index 2fe4c06b..5d0c8d0b 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor @@ -166,8 +166,8 @@ else { var stream = await Access.DownloadStream(x); await ToastService.Info(SmartTranslateService.Translate("Starting download")); - await FileService.AddBuffer(stream); - await FileService.DownloadBinaryBuffers(x.Name); + stream.Position = 0; + await FileService.DownloadFile(fileName: x.Name, stream: stream, contentType: "application/octet-stream"); } catch (NotImplementedException) { From 4a8605a5543ec8cbaec57f03bee76c23ee5e5178 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 17:30:26 +0200 Subject: [PATCH 09/45] Implemented databases --- .../App/Models/Forms/DatabaseDataModel.cs | 17 +++ .../Models/Plesk/Requests/CreateDatabase.cs | 23 +++ .../Plesk/Requests/CreateDatabaseUser.cs | 15 ++ .../App/Models/Plesk/Resources/Database.cs | 15 ++ .../Models/Plesk/Resources/DatabaseServer.cs | 30 ++++ .../Models/Plesk/Resources/DatabaseUser.cs | 15 ++ Moonlight/App/Services/WebsiteService.cs | 96 +++++++++++++ .../ErrorBoundaries/SoftErrorBoundary.razor | 4 + .../WebsiteControl/WebsiteDatabases.razor | 135 ++++++++++++++++++ Moonlight/Shared/Views/Website/Index.razor | 1 + Moonlight/resources/lang/de_de.lang | 6 + Moonlight/resources/lang/en_us.lang | 21 +++ 12 files changed, 378 insertions(+) create mode 100644 Moonlight/App/Models/Forms/DatabaseDataModel.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/Database.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor create mode 100644 Moonlight/resources/lang/en_us.lang diff --git a/Moonlight/App/Models/Forms/DatabaseDataModel.cs b/Moonlight/App/Models/Forms/DatabaseDataModel.cs new file mode 100644 index 00000000..b298512d --- /dev/null +++ b/Moonlight/App/Models/Forms/DatabaseDataModel.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class DatabaseDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MinLength(8, ErrorMessage = "The name should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The database name should be maximal 32 characters")] + [RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only contain of lower case characters and numbers")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to enter a password")] + [MinLength(8, ErrorMessage = "The password should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The password name should be maximal 32 characters")] + public string Password { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs new file mode 100644 index 00000000..11c9a0cb --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabase +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("parent_domain")] public ParentDomainModel ParentDomain { get; set; } = new(); + + [JsonProperty("server_id")] + public int ServerId { get; set; } + + public class ParentDomainModel + { + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs new file mode 100644 index 00000000..eca5dd38 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabaseUser +{ + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("password")] + public string Password { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/Database.cs b/Moonlight/App/Models/Plesk/Resources/Database.cs new file mode 100644 index 00000000..27a78993 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/Database.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class Database +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs new file mode 100644 index 00000000..a16ceabd --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseServer +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("host")] + public string Host { get; set; } + + [JsonProperty("port")] + public int Port { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("db_count")] + public int DbCount { get; set; } + + [JsonProperty("is_default")] + public bool IsDefault { get; set; } + + [JsonProperty("is_local")] + public bool IsLocal { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs new file mode 100644 index 00000000..033a763b --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseUser +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs index ad85d488..11906034 100644 --- a/Moonlight/App/Services/WebsiteService.cs +++ b/Moonlight/App/Services/WebsiteService.cs @@ -130,10 +130,21 @@ public class WebsiteService 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) { @@ -147,6 +158,7 @@ public class WebsiteService return user.Id; } + #region SSL public async Task GetSslCertificates(Website w) { var website = EnsureData(w); @@ -241,6 +253,90 @@ public class WebsiteService 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) { diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index cbad558c..7f561201 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -43,6 +43,10 @@ pleskException.Message ); } + else if (exception is NotImplementedException) + { + await AlertService.Error(SmartTranslateService.Translate("This function is not implemented")); + } else { throw exception; diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor new file mode 100644 index 00000000..3679564f --- /dev/null +++ b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor @@ -0,0 +1,135 @@ +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms +@using Moonlight.App.Models.Plesk.Resources +@using Moonlight.App.Services + +@inject SmartTranslateService SmartTranslateService +@inject WebsiteService WebsiteService + +
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+ @if (Databases.Any()) + { +
+ @foreach (var database in Databases) + { +
+

+ +

+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+ } +
+ } + else + { +
+ No databases found for this website +
+ } +
+
+ +
+ +@code +{ + [CascadingParameter] + public Website CurrentWebsite { get; set; } + + private LazyLoader LazyLoader; + private Database[] Databases; + private DatabaseServer DatabaseServer; + private string Host; + + private DatabaseDataModel Model = new(); + + private async Task Load(LazyLoader arg) + { + Databases = await WebsiteService.GetDatabases(CurrentWebsite); + + if (Databases.Any()) + { + DatabaseServer = (await WebsiteService.GetDefaultDatabaseServer(CurrentWebsite))!; + Host = await WebsiteService.GetHost(CurrentWebsite); + } + } + + private async Task OnValidSubmit() + { + await WebsiteService.CreateDatabase(CurrentWebsite, Model.Name, Model.Password); + Model = new(); + await LazyLoader.Reload(); + } + + private async Task DeleteDatabase(Database database) + { + await WebsiteService.DeleteDatabase(CurrentWebsite, database); + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Website/Index.razor b/Moonlight/Shared/Views/Website/Index.razor index cc21a6b8..7b1a8cd3 100644 --- a/Moonlight/Shared/Views/Website/Index.razor +++ b/Moonlight/Shared/Views/Website/Index.razor @@ -62,6 +62,7 @@ break; case "databases": + break; default: diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index a4f49ceb..1d9fd9c9 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -506,3 +506,9 @@ Api url;Api url Host system offline;Host system offline The host system the website is running on is currently offline;The host system the website is running on is currently offline No SSL certificates found;No SSL certificates found +No databases found for this website;No databases found for this website +The name should be at least 8 characters long;The name should be at least 8 characters long +The name should only contain of lower case characters and numbers;The name should only contain of lower case characters and numbers +Error from plesk;Error from plesk +Host;Host +Username;Username diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/resources/lang/en_us.lang new file mode 100644 index 00000000..75bac759 --- /dev/null +++ b/Moonlight/resources/lang/en_us.lang @@ -0,0 +1,21 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Create;Create +Server;Server +Domain;Domain +Website;Website +Login;Login +Register;Register +Email;Email +Password;Password +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Sign in with Google;Sign in with Google +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up From 936e35c81d8421fb6a18bb2d94c46b008cdf3ac2 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 19:02:29 +0200 Subject: [PATCH 10/45] Update de_de.lang --- Moonlight/resources/lang/de_de.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 1d9fd9c9..c0ba8c4f 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -512,3 +512,4 @@ The name should only contain of lower case characters and numbers;The name shoul Error from plesk;Error from plesk Host;Host Username;Username +SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one From 9332029c4a0dd9533e9698f1d20c5b30c974b460 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 19:20:28 +0200 Subject: [PATCH 11/45] Removed legacy database ui --- .../Components/Partials/SidebarMenu.razor | 8 -- Moonlight/Shared/Views/Admin/Index.razor | 34 +++------ Moonlight/Shared/Views/Index.razor | 76 +++++-------------- 3 files changed, 30 insertions(+), 88 deletions(-) diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index 2d82030e..c7e6c692 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -52,14 +52,6 @@ else Websites
-
@@ -50,27 +53,6 @@
-
-
-
-
-
-
- Databases -
- - @(DatabaseCount) - -
-
- - - -
-
-
-
-
@@ -80,7 +62,7 @@ Domains - 0 + @(DomainCount)
-
-
-
-
-
-
- Databases -
- - @(DatabaseCount) - -
-
- - - -
-
-
-
-
-
-
- -
-
- - Create a database - - - A quick way to store your data and manage it from all around the world - -
-
-
@@ -188,20 +157,6 @@
-
-
- -
-
- - Manage your databases - - - Insert, delete and update the data in your databases - -
-
-
@@ -239,16 +194,27 @@ [CascadingParameter] public User User { get; set; } - private int DatabaseCount = 0; private int ServerCount = 0; + private int DomainCount = 0; + private int WebsiteCount = 0; - private async Task Load(LazyLoader lazyLoader) + private Task Load(LazyLoader lazyLoader) { - DatabaseCount = 0; - ServerCount = ServerRepository .Get() .Include(x => x.Owner) .Count(x => x.Owner.Id == User.Id); + + DomainCount = DomainRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id); + + WebsiteCount = WebsiteRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id); + + return Task.CompletedTask; } } \ No newline at end of file From c18d683fb8b99bb0bfd76cae38f210dbd4dcbbe2 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 19:33:34 +0200 Subject: [PATCH 12/45] Replaced legacy dependency resolve with server service function for wingsfileaccess --- .../Settings/JavascriptFileSetting.razor | 12 +++--------- .../ServerControl/Settings/PythonFileSetting.razor | 11 +++-------- .../ServerControl/Settings/ServerResetSetting.razor | 12 ++---------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/Moonlight/Shared/Components/ServerControl/Settings/JavascriptFileSetting.razor b/Moonlight/Shared/Components/ServerControl/Settings/JavascriptFileSetting.razor index 1d97402b..2787979c 100644 --- a/Moonlight/Shared/Components/ServerControl/Settings/JavascriptFileSetting.razor +++ b/Moonlight/Shared/Components/ServerControl/Settings/JavascriptFileSetting.razor @@ -2,13 +2,12 @@ @using Moonlight.App.Repositories.Servers @using Moonlight.Shared.Components.FileManagerPartials @using Moonlight.App.Database.Entities -@using Moonlight.App.Helpers @using Moonlight.App.Helpers.Files @using Moonlight.App.Services @inject ServerRepository ServerRepository -@inject WingsApiHelper WingsApiHelper @inject SmartTranslateService SmartTranslateService +@inject ServerService ServerService
@@ -42,14 +41,9 @@ private FileSelectModal FileSelectModal; private LazyLoader LazyLoader; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - Access = new WingsFileAccess(WingsApiHelper, - null!, - CurrentServer, - null!, - null! - ); + Access = await ServerService.CreateFileAccess(CurrentServer, null!); } private async Task Load(LazyLoader lazyLoader) diff --git a/Moonlight/Shared/Components/ServerControl/Settings/PythonFileSetting.razor b/Moonlight/Shared/Components/ServerControl/Settings/PythonFileSetting.razor index 80e8ea9b..7cc041ff 100644 --- a/Moonlight/Shared/Components/ServerControl/Settings/PythonFileSetting.razor +++ b/Moonlight/Shared/Components/ServerControl/Settings/PythonFileSetting.razor @@ -7,7 +7,7 @@ @using Moonlight.App.Services @inject ServerRepository ServerRepository -@inject WingsApiHelper WingsApiHelper +@inject ServerService ServerService @inject SmartTranslateService SmartTranslateService
@@ -42,14 +42,9 @@ private FileSelectModal FileSelectModal; private LazyLoader LazyLoader; - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - Access = new WingsFileAccess(WingsApiHelper, - null!, - CurrentServer, - null!, - null! - ); + Access = await ServerService.CreateFileAccess(CurrentServer, null!); } private async Task Load(LazyLoader lazyLoader) diff --git a/Moonlight/Shared/Components/ServerControl/Settings/ServerResetSetting.razor b/Moonlight/Shared/Components/ServerControl/Settings/ServerResetSetting.razor index 0af95510..61795469 100644 --- a/Moonlight/Shared/Components/ServerControl/Settings/ServerResetSetting.razor +++ b/Moonlight/Shared/Components/ServerControl/Settings/ServerResetSetting.razor @@ -7,8 +7,6 @@ @inject SmartTranslateService SmartTranslateService @inject AlertService AlertService @inject ToastService ToastService -@inject WingsApiHelper WingsApiHelper -@inject ConfigService ConfigService @inject ServerService ServerService Date: Thu, 6 Apr 2023 20:05:44 +0200 Subject: [PATCH 13/45] statistics service --- Moonlight/App/Database/DataContext.cs | 1 + .../App/Database/Entities/StatisticsData.cs | 8 + ...20230406161820_AddedStatistics.Designer.cs | 894 ++++++++++++++++++ .../20230406161820_AddedStatistics.cs | 38 + .../Migrations/DataContextModelSnapshot.cs | 18 + .../App/Repositories/StatisticsRepository.cs | 37 + .../Statistics/StatisticsCaptureService.cs | 57 ++ Moonlight/Program.cs | 5 + 8 files changed, 1058 insertions(+) create mode 100644 Moonlight/App/Database/Entities/StatisticsData.cs create mode 100644 Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs create mode 100644 Moonlight/App/Repositories/StatisticsRepository.cs create mode 100644 Moonlight/App/Services/Statistics/StatisticsCaptureService.cs diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 58bd3db4..2cfb7d9e 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -41,6 +41,7 @@ public class DataContext : DbContext public DbSet Subscriptions { get; set; } public DbSet PleskServers { get; set; } public DbSet Websites { get; set; } + public DbSet Statistics { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/StatisticsData.cs b/Moonlight/App/Database/Entities/StatisticsData.cs new file mode 100644 index 00000000..1cef632d --- /dev/null +++ b/Moonlight/App/Database/Entities/StatisticsData.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Database.Entities; + +public class StatisticsData +{ + public int Id { get; set; } + public string Chart { get; set; } + public double Value { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs new file mode 100644 index 00000000..6345ccb1 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs @@ -0,0 +1,894 @@ +// +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("20230406161820_AddedStatistics")] + partial class AddedStatistics + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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("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"); + + 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.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.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") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs new file mode 100644 index 00000000..b00cb9cc --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedStatistics : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Statistics", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Chart = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "double", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Statistics", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Statistics"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 14331192..a249a969 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -571,6 +571,24 @@ namespace Moonlight.App.Database.Migrations 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("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => { b.Property("Id") diff --git a/Moonlight/App/Repositories/StatisticsRepository.cs b/Moonlight/App/Repositories/StatisticsRepository.cs new file mode 100644 index 00000000..d118e71e --- /dev/null +++ b/Moonlight/App/Repositories/StatisticsRepository.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class StatisticsRepository : IDisposable +{ + private readonly DataContext DataContext; + + public StatisticsRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.Statistics; + } + + public StatisticsData Add(StatisticsData data) + { + var x = DataContext.Statistics.Add(data); + DataContext.SaveChanges(); + return x.Entity; + } + + public StatisticsData Add(string chart, double value) + { + return Add(new StatisticsData() {Chart = chart, Value = value}); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs new file mode 100644 index 00000000..3098e5d1 --- /dev/null +++ b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs @@ -0,0 +1,57 @@ +using Moonlight.App.Database; +using Moonlight.App.Repositories; + +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; + + public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService) + { + ServiceScopeFactory = serviceScopeFactory; + var provider = ServiceScopeFactory.CreateScope().ServiceProvider; + + DataContext = provider.GetRequiredService(); + ConfigService = configService; + StatisticsRepository = provider.GetRequiredService(); + WebsiteService = provider.GetRequiredService(); + PleskServerRepository = provider.GetRequiredService(); + + var config = ConfigService.GetSection("Moonlight").GetSection("Statistics"); + if(!config.GetValue("Enabled")) + return; + + var _period = config.GetValue("Wait"); + var period = TimeSpan.FromMinutes(_period); + Timer = new(period); + + Task.Run(Run); + } + + private async Task Run() + { + while (await Timer.WaitForNextTickAsync()) + { + 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()) + { + databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount; + } + + StatisticsRepository.Add("statistics.databasesCount", databases); + } + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 831312f2..f9134177 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -16,6 +16,7 @@ using Moonlight.App.Services.LogServices; 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; namespace Moonlight @@ -64,6 +65,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -128,6 +131,7 @@ namespace Moonlight // Background services builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // Third party services builder.Services.AddBlazorTable(); @@ -160,6 +164,7 @@ namespace Moonlight // AutoStart services _ = app.Services.GetRequiredService(); _ = app.Services.GetRequiredService(); + _ = app.Services.GetRequiredService(); // Discord bot service //var discordBotService = app.Services.GetRequiredService(); From e194e33d814162fed4150a6476928895e3678a7d Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:23:45 +0200 Subject: [PATCH 14/45] Statistics Ui --- .../App/Database/Entities/StatisticsData.cs | 4 + ...6182554_ChangedStatisticsModel.Designer.cs | 975 ++++++++++++++++++ .../20230406182554_ChangedStatisticsModel.cs | 30 + .../Migrations/DataContextModelSnapshot.cs | 3 + .../App/Models/Misc/StatisticsTimeSpan.cs | 10 + .../App/Repositories/StatisticsRepository.cs | 2 +- .../Statistics/StatisticsViewService.cs | 24 + Moonlight/Moonlight.csproj | 1 + Moonlight/Pages/_Layout.cshtml | 2 + Moonlight/Program.cs | 1 + Moonlight/Shared/Views/Admin/Statistics.razor | 128 +++ Moonlight/resources/lang/de_de.lang | 5 + 12 files changed, 1184 insertions(+), 1 deletion(-) create mode 100644 Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs create mode 100644 Moonlight/App/Models/Misc/StatisticsTimeSpan.cs create mode 100644 Moonlight/App/Services/Statistics/StatisticsViewService.cs create mode 100644 Moonlight/Shared/Views/Admin/Statistics.razor diff --git a/Moonlight/App/Database/Entities/StatisticsData.cs b/Moonlight/App/Database/Entities/StatisticsData.cs index 1cef632d..64f8ea7d 100644 --- a/Moonlight/App/Database/Entities/StatisticsData.cs +++ b/Moonlight/App/Database/Entities/StatisticsData.cs @@ -3,6 +3,10 @@ public class StatisticsData { public int Id { get; set; } + public string Chart { get; set; } + public double Value { get; set; } + + public DateTime Date { get; set; } } \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs new file mode 100644 index 00000000..73544a6e --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs @@ -0,0 +1,975 @@ +// +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("20230406182554_ChangedStatisticsModel")] + partial class ChangedStatisticsModel + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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"); + + 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.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.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") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs new file mode 100644 index 00000000..24d619e9 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class ChangedStatisticsModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Date", + table: "Statistics", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Date", + table: "Statistics"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index a249a969..96bc5cb9 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -581,6 +581,9 @@ namespace Moonlight.App.Database.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("Date") + .HasColumnType("datetime(6)"); + b.Property("Value") .HasColumnType("double"); diff --git a/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs b/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs new file mode 100644 index 00000000..6341be6f --- /dev/null +++ b/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Models.Misc; + +public enum StatisticsTimeSpan +{ + Hour = 1, + Day = 24, + Month = Day * 31, + Year = 365 * Day, + AllTime = Year * 99 +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/StatisticsRepository.cs b/Moonlight/App/Repositories/StatisticsRepository.cs index d118e71e..f33751b7 100644 --- a/Moonlight/App/Repositories/StatisticsRepository.cs +++ b/Moonlight/App/Repositories/StatisticsRepository.cs @@ -27,7 +27,7 @@ public class StatisticsRepository : IDisposable public StatisticsData Add(string chart, double value) { - return Add(new StatisticsData() {Chart = chart, Value = value}); + return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTime.Now}); } public void Dispose() diff --git a/Moonlight/App/Services/Statistics/StatisticsViewService.cs b/Moonlight/App/Services/Statistics/StatisticsViewService.cs new file mode 100644 index 00000000..329445fe --- /dev/null +++ b/Moonlight/App/Services/Statistics/StatisticsViewService.cs @@ -0,0 +1,24 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Misc; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services.Statistics; + +public class StatisticsViewService +{ + private readonly StatisticsRepository StatisticsRepository; + + public StatisticsViewService(StatisticsRepository statisticsRepository) + { + StatisticsRepository = statisticsRepository; + } + + public StatisticsData[] GetData(string chart, StatisticsTimeSpan timeSpan) + { + var startDate = DateTime.Now - TimeSpan.FromHours((int)timeSpan); + + var objs = StatisticsRepository.Get().Where(x => x.Date > startDate && x.Chart == chart); + + return objs.ToArray(); + } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 816407f9..50e4901c 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -9,6 +9,7 @@ + diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index b82364f4..2b6904ec 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -114,5 +114,7 @@ + + \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index f9134177..1a0b45d4 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -96,6 +96,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Shared/Views/Admin/Statistics.razor b/Moonlight/Shared/Views/Admin/Statistics.razor new file mode 100644 index 00000000..49c5f434 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Statistics.razor @@ -0,0 +1,128 @@ +@page "/admin/statistics" +@using Moonlight.App.Models.Misc +@using Moonlight.App.Services.Statistics +@using Moonlight.App.Database.Entities +@using ApexCharts + +@inject StatisticsViewService StatisticsViewService + + +
+
+
+ +
+
+
+ + + @foreach (var charts in Charts.Chunk(2)) + { +
+ @foreach (var chart in charts) + { +
+
+
+
+ @chart.Header +
+
+
+ + + +
+
+
+ } +
+ } +
+
+ +@code { + private StatisticsTimeSpan StatisticsTimeSpan = StatisticsTimeSpan.Day; + private LazyLoader Loader; + private List<(string Header, StatisticsData[] Data)> Charts = new(); + + private int bind + { + get { return (int) StatisticsTimeSpan; } + set { StatisticsTimeSpan = (StatisticsTimeSpan) value; + Task.Run(async() => await Loader.Reload()); + } + } + + private async Task Load(LazyLoader loader) + { + Charts.Clear(); + + Charts.Add(("Servers", StatisticsViewService.GetData("statistics.serversCount", StatisticsTimeSpan))); + Charts.Add(("Users", StatisticsViewService.GetData("statistics.usersCount", StatisticsTimeSpan))); + Charts.Add(("Domains", StatisticsViewService.GetData("statistics.domainsCount", StatisticsTimeSpan))); + Charts.Add(("Databases", StatisticsViewService.GetData("statistics.databasesCount", StatisticsTimeSpan))); + Charts.Add(("Websites", StatisticsViewService.GetData("statistics.websitesCount", StatisticsTimeSpan))); + } + + private string FormatDate(DateTime e) + { + string i2s(int i) + { + if (i.ToString().Length < 2) + return "0" + i; + return i.ToString(); + } + + return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year} {i2s(e.Hour)}:{i2s(e.Minute)}"; + } + + private ApexChartOptions GenerateOptions() + { + return new() + { + Legend = new Legend() + { + Show = false + }, + DataLabels = new DataLabels() + { + Enabled = false + }, + Xaxis = new XAxis() + { + Labels = new XAxisLabels() + { + Show = false + } + }, + Chart = new Chart() + { + RedrawOnParentResize = true, + Toolbar = new Toolbar() + { + Show = false + }, + Height = 300 + } + }; + } + + private async Task OnChartRendered() + { + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 1d9fd9c9..4968cc0c 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -512,3 +512,8 @@ The name should only contain of lower case characters and numbers;The name shoul Error from plesk;Error from plesk Host;Host Username;Username +Hour;Hour +Day;Day +Month;Month +Year;Year +All time;All time From d43f77ea47bf1c12440e7319cc2f17ae5e405d10 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:58:26 +0200 Subject: [PATCH 15/45] host file access + use in admin resource manager --- Moonlight/App/Helpers/Files/HostFileAccess.cs | 143 ++++++++++++++++++ .../Shared/Views/Admin/Sys/Resources.razor | 6 +- Moonlight/resources/lang/de_de.lang | 5 + 3 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 Moonlight/App/Helpers/Files/HostFileAccess.cs diff --git a/Moonlight/App/Helpers/Files/HostFileAccess.cs b/Moonlight/App/Helpers/Files/HostFileAccess.cs new file mode 100644 index 00000000..0c425a8b --- /dev/null +++ b/Moonlight/App/Helpers/Files/HostFileAccess.cs @@ -0,0 +1,143 @@ +using System.IO.Compression; + +namespace Moonlight.App.Helpers.Files; + +public class HostFileAccess : FileAccess +{ + private string BasePath; + + public HostFileAccess(string basePath) + { + BasePath = basePath; + } + private string currhp => BasePath.TrimEnd('/') + "/" + CurrentPath.TrimStart('/').TrimEnd('/') + "/"; + public override async Task Ls() + { + var x = new List(); + + foreach (var dir in Directory.GetDirectories(currhp)) + { + x.Add(new() + { + Name = dir.Remove(0, currhp.Length), + Size = 0, + IsFile = false, + }); + } + + foreach (var fn in Directory.GetFiles(currhp)) + { + x.Add(new() + { + Name = fn.Remove(0, currhp.Length), + Size = new FileInfo(fn).Length, + IsFile = true, + }); + } + + return 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 async Task Read(FileData fileData) + { + return await File.ReadAllTextAsync(currhp + fileData.Name); + } + + public override async Task Write(FileData fileData, string content) + { + await File.WriteAllTextAsync(currhp + fileData.Name, content); + } + + public override async Task Upload(string name, Stream dataStream, Action? progressUpdated = null) + { + var ms = new MemoryStream(); + await dataStream.CopyToAsync(ms); + var data = ms.ToArray(); + ms.Dispose(); + dataStream.Dispose(); + + await File.WriteAllBytesAsync(currhp + name, data); + } + + public override async Task MkDir(string name) + { + Directory.CreateDirectory(currhp + name + "/"); + } + + public override Task Pwd() + { + return Task.FromResult(CurrentPath); + } + + public override Task DownloadUrl(FileData fileData) + { + throw new NotImplementedException(); + } + + public override async Task DownloadStream(FileData fileData) + { + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + + s.Write(File.ReadAllBytes(currhp + fileData.Name)); + s.Position = 0; + + return s; + } + + public override async Task Delete(FileData fileData) + { + if (fileData.IsFile) + File.Delete(currhp + fileData.Name); + else + Directory.Delete(currhp + fileData.Name); + } + + public override async Task Move(FileData fileData, string newPath) + { + if (fileData.IsFile) + File.Move(currhp + fileData.Name, newPath); + else + Directory.Move(currhp + fileData.Name, newPath); + } + + public override Task Compress(params FileData[] files) + { + throw new NotImplementedException(); + } + + public override Task Decompress(FileData fileData) + { + throw new NotImplementedException(); + } + + public override Task GetLaunchUrl() + { + throw new NotImplementedException(); + } + + public override object Clone() + { + return new HostFileAccess(BasePath); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/Resources.razor b/Moonlight/Shared/Views/Admin/Sys/Resources.razor index cbb4fd54..28a53119 100644 --- a/Moonlight/Shared/Views/Admin/Sys/Resources.razor +++ b/Moonlight/Shared/Views/Admin/Sys/Resources.razor @@ -1,14 +1,14 @@ @page "/admin/system/resources" -@using Moonlight.Shared.Components.Navigations @using Moonlight.Shared.Components.FileManagerPartials -@using Moonlight.App.Models.Files.Accesses +@using Moonlight.App.Helpers.Files +@using Moonlight.Shared.Components.Navigations
- +
\ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index c0ba8c4f..8b2d39c4 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -513,3 +513,8 @@ Error from plesk;Error from plesk Host;Host Username;Username SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one +Hour;Hour +Day;Day +Month;Month +Year;Year +All time;All time From 8bc4fe642437851b1e2f494738cda204d9613a5d Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 6 Apr 2023 23:07:33 +0200 Subject: [PATCH 16/45] Update de_de.lang --- Moonlight/resources/lang/de_de.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 8b2d39c4..8e55d55a 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -518,3 +518,4 @@ Day;Day Month;Month Year;Year All time;All time +This function is not implemented;This function is not implemented From 4253f94f596f92ba9b7c404b19fcbc462d545fb5 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 10 Apr 2023 03:40:08 +0200 Subject: [PATCH 17/45] Removed old typeahead. Added own solution. Lang file, notes --- Moonlight/App/Helpers/WingsServerConverter.cs | 2 +- Moonlight/App/Models/Forms/DomainDataModel.cs | 18 + Moonlight/App/Models/Forms/ServerDataModel.cs | 30 ++ Moonlight/App/Models/Forms/TestDataModel.cs | 10 + Moonlight/Moonlight.csproj | 3 +- Moonlight/Pages/_Layout.cshtml | 4 +- .../Components/Forms/SmartDropdown.razor | 93 +++++ .../Shared/Components/Forms/SmartSelect.razor | 6 +- .../Shared/Views/Admin/Domains/New.razor | 120 ++---- .../Shared/Views/Admin/Servers/New.razor | 350 ++++++++---------- .../Shared/Views/Admin/Websites/New.razor | 27 +- Moonlight/Shared/Views/Test.razor | 47 +-- Moonlight/resources/lang/de_de.lang | 12 + 13 files changed, 394 insertions(+), 328 deletions(-) create mode 100644 Moonlight/App/Models/Forms/DomainDataModel.cs create mode 100644 Moonlight/App/Models/Forms/ServerDataModel.cs create mode 100644 Moonlight/App/Models/Forms/TestDataModel.cs create mode 100644 Moonlight/Shared/Components/Forms/SmartDropdown.razor diff --git a/Moonlight/App/Helpers/WingsServerConverter.cs b/Moonlight/App/Helpers/WingsServerConverter.cs index acc7ed17..f1ebbc29 100644 --- a/Moonlight/App/Helpers/WingsServerConverter.cs +++ b/Moonlight/App/Helpers/WingsServerConverter.cs @@ -46,7 +46,7 @@ public class WingsServerConverter } // Build - wingsServer.Settings.Build.Swap = server.Memory * 2; + wingsServer.Settings.Build.Swap = server.Memory * 2; //TODO: Add config option wingsServer.Settings.Build.Threads = null!; wingsServer.Settings.Build.Cpu_Limit = server.Cpu; wingsServer.Settings.Build.Disk_Space = server.Disk; diff --git a/Moonlight/App/Models/Forms/DomainDataModel.cs b/Moonlight/App/Models/Forms/DomainDataModel.cs new file mode 100644 index 00000000..ce1bbc5a --- /dev/null +++ b/Moonlight/App/Models/Forms/DomainDataModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class DomainDataModel +{ + [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")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a shared domain")] + public SharedDomain SharedDomain { get; set; } + + [Required(ErrorMessage = "You need to specify a owner")] + public User Owner { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/ServerDataModel.cs b/Moonlight/App/Models/Forms/ServerDataModel.cs new file mode 100644 index 00000000..1b8c5008 --- /dev/null +++ b/Moonlight/App/Models/Forms/ServerDataModel.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class ServerDataModel +{ + [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 owner")] + public User Owner { get; set; } + + [Required(ErrorMessage = "You need to specify cpu amount")] + public int Cpu { get; set; } = 100; + + [Required(ErrorMessage = "You need to specify a memory amount")] + public int Memory { get; set; } = 1024; + + [Required(ErrorMessage = "You need to specify a disk amount")] + public int Disk { get; set; } = 1024; + + [Required(ErrorMessage = "You need to specify a image")] + public Image Image { get; set; } + + public string OverrideStartup { get; set; } = ""; + + public int DockerImageIndex { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/TestDataModel.cs b/Moonlight/App/Models/Forms/TestDataModel.cs new file mode 100644 index 00000000..0fecab72 --- /dev/null +++ b/Moonlight/App/Models/Forms/TestDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class TestDataModel +{ + [Required] + public User User { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 816407f9..be85ac28 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -9,12 +9,10 @@ - - @@ -24,6 +22,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index b82364f4..9a6aee13 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -45,8 +45,7 @@ - - + @@ -87,7 +86,6 @@ - diff --git a/Moonlight/Shared/Components/Forms/SmartDropdown.razor b/Moonlight/Shared/Components/Forms/SmartDropdown.razor new file mode 100644 index 00000000..868d035a --- /dev/null +++ b/Moonlight/Shared/Components/Forms/SmartDropdown.razor @@ -0,0 +1,93 @@ +@typeparam T +@using Logging.Net +@inherits InputBase + + + +@code { + + [Parameter] + public IEnumerable Items { get; set; } + + [Parameter] + public Func DisplayFunc { get; set; } + + [Parameter] + public Func SearchProp { get; set; } + + private string SearchTerm + { + get => searchTerm; + set + { + FilteredItems = Items.Where(i => SearchProp(i).Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + searchTerm = value; + } + } + + private string searchTerm = ""; + + private List FilteredItems = new(); + + private void SelectItem(T item) + { + CurrentValue = item; + SearchTerm = ""; + FilteredItems.Clear(); + } + + protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage) + { + // Check if the value is null or empty + if (string.IsNullOrEmpty(value)) + { + result = default(T)!; + validationErrorMessage = "Value cannot be null or empty"; + return false; + } + + // Try to find an item that matches the search term + var item = FilteredItems.FirstOrDefault(i => SearchProp(i).Equals(value, StringComparison.OrdinalIgnoreCase)); + if (item != null) + { + result = item; + validationErrorMessage = null; + return true; + } + else + { + result = default(T)!; + validationErrorMessage = $"No item found for search term '{value}'"; + return false; + } + } + +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Forms/SmartSelect.razor b/Moonlight/Shared/Components/Forms/SmartSelect.razor index ea246825..970f1800 100644 --- a/Moonlight/Shared/Components/Forms/SmartSelect.razor +++ b/Moonlight/Shared/Components/Forms/SmartSelect.razor @@ -11,10 +11,13 @@ @code { [Parameter] - public TField[] Items { get; set; } + public IEnumerable Items { get; set; } [Parameter] public Func DisplayField { get; set; } + + [Parameter] + public Func? OnChange { get; set; } protected override void OnInitialized() { @@ -53,6 +56,7 @@ { Value = i; ValueChanged.InvokeAsync(i); + OnChange?.Invoke(); } } } diff --git a/Moonlight/Shared/Views/Admin/Domains/New.razor b/Moonlight/Shared/Views/Admin/Domains/New.razor index a15a8239..203456ad 100644 --- a/Moonlight/Shared/Views/Admin/Domains/New.razor +++ b/Moonlight/Shared/Views/Admin/Domains/New.razor @@ -1,66 +1,51 @@ @page "/admin/domains/new" @using Moonlight.App.Services @using Moonlight.App.Database.Entities -@using Blazored.Typeahead +@using Moonlight.App.Models.Forms @using Moonlight.App.Repositories @using Moonlight.App.Repositories.Domains +@using Mappy.Net @inject SmartTranslateService SmartTranslateService @inject SharedDomainRepository SharedDomainRepository @inject DomainRepository DomainRepository @inject UserRepository UserRepository +@inject NavigationManager NavigationManager
- -
- - - - -
-
+ - -
-
-
- - - @(context.Email) - - - @(context.Email) - - +
+ + + +
-
- - +
+ + + +
+
+ + +
+ +
@@ -68,54 +53,27 @@ @code { - private string Name; + private DomainDataModel Model = new(); - private User? User; private User[] Users; - - private List SharedDomains; - - private SharedDomain? SharedDomain; - - private int SharedDomainId - { - get => SharedDomain?.Id ?? -1; - set - { - SharedDomain = SharedDomains.FirstOrDefault(x => x.Id == value); - InvokeAsync(StateHasChanged); - } - } - - private Task> SearchUsers(string input) - { - if (string.IsNullOrEmpty(input)) - { - return Task.FromResult(Array.Empty().Cast()); - } - else - { - return Task.FromResult(Users.Where(x => x.Email.ToLower().StartsWith(input))); - } - } + private SharedDomain[] SharedDomains; private Task Load(LazyLoader lazyLoader) { Users = UserRepository.Get().ToArray(); - SharedDomains = SharedDomainRepository.Get().ToList(); + SharedDomains = SharedDomainRepository.Get().ToArray(); return Task.CompletedTask; } private Task Add() { - DomainRepository.Add(new() - { - Name = Name.ToLower(), - Owner = User!, - SharedDomain = SharedDomain! - }); - + var domain = Mapper.Map(Model); + + DomainRepository.Add(domain); + + NavigationManager.NavigateTo("/admin/domains"); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Servers/New.razor b/Moonlight/Shared/Views/Admin/Servers/New.razor index 82093d5e..6c81dc72 100644 --- a/Moonlight/Shared/Views/Admin/Servers/New.razor +++ b/Moonlight/Shared/Views/Admin/Servers/New.razor @@ -6,7 +6,7 @@ @using Moonlight.App.Exceptions @using Moonlight.App.Services.Interop @using Logging.Net -@using Blazored.Typeahead +@using Moonlight.App.Models.Forms @inject NodeRepository NodeRepository @inject ImageRepository ImageRepository @@ -19,226 +19,182 @@ -
-
- -
- - - - -
- -
-
- - - @(context.Email) - - - @(context.Email) - - -
-
-
-
-
-
- -
- - - - - - CPU Cores (100% = 1 Core) - -
- -
- - - - - - MB - -
- -
- - - - - - MB - -
-
-
-
-
- - - @if (Image != null) - { + +
+
- + - +
- - } -
-
- -
-
- @if (Image != null) - { -
- @foreach (var vars in ServerVariables.Chunk(4)) - { -
- @foreach (var variable in vars) - { -
-
- -
- -
- -
- -
-
-
- } -
- } +
+ +
- } -
-
- -
-
-
-
+
+
+ +
+ + + + + + CPU Cores (100% = 1 Core) + +
+ +
+ + + + + + MB + +
+ +
+ + + + + + MB + +
+
+
+
+
+ +
+ +
+ @if (Model.Image != null) + { + +
+ + + + +
+ + + @foreach (var image in Model.Image.DockerImages) + { + + } + + } +
+
+ +
+
+ @if (Model.Image != null) + { +
+ @foreach (var vars in ServerVariables.Chunk(3)) + { +
+ @foreach (var variable in vars) + { +
+
+ +
+ +
+ +
+ +
+
+
+ } +
+ } +
+ } +
+
+ +
+
+
+ + Cancel + + +
+
+
+ @code { + private ServerDataModel Model = new(); + private List Images; private Node[] Nodes; private User[] Users; - private string Name = ""; - - private int Cpu = 100; - private int Memory = 4096; - private int Disk = 10240; - - private string OverrideStartup = ""; - private int DockerImageIndex = 0; - - private Image? Image; - private User? User; - private ServerVariable[] ServerVariables = Array.Empty(); - private int ImageIndex - { - get => Image == null ? 0 : Images.IndexOf(Image); - set - { - Image = Images[value]; - - if (Image == null) - ServerVariables = Array.Empty(); - else - RebuildVariables(); - - - InvokeAsync(StateHasChanged); - } - } - private void RebuildVariables() { var list = new List(); - foreach (var variable in Image.Variables) + if (Model.Image != null) { - list.Add(new() + foreach (var variable in Model.Image.Variables) { - Key = variable.Key, - Value = variable.DefaultValue - }); + list.Add(new() + { + Key = variable.Key, + Value = variable.DefaultValue + }); + } } ServerVariables = list.ToArray(); } - private Task> SearchUsers(string input) - { - if (string.IsNullOrEmpty(input)) - { - return Task.FromResult(Array.Empty().Cast()); - } - else - { - return Task.FromResult(Users.Where(x => x.Email.ToLower().StartsWith(input))); - } - } - private async Task Load(LazyLoader lazyLoader) { await lazyLoader.SetText("Loading images"); @@ -256,24 +212,18 @@ await lazyLoader.SetText("Loading users"); Users = UserRepository.Get().ToArray(); - User = Users.FirstOrDefault(); - - Image = Images.FirstOrDefault(); RebuildVariables(); - - if (Image != null) - DockerImageIndex = Image.DockerImages.Count - 1; } private async Task Create() { try { - await ServerService.Create(Name, Cpu, Memory, Disk, User, Image, null, server => + await ServerService.Create(Model.Name, Model.Cpu, Model.Memory, Model.Disk, Model.Owner, Model.Image, null, server => { - server.OverrideStartup = OverrideStartup; - server.DockerImageIndex = DockerImageIndex; + server.OverrideStartup = Model.OverrideStartup; + server.DockerImageIndex = Model.DockerImageIndex; foreach (var serverVariable in ServerVariables) { @@ -303,4 +253,10 @@ ); } } + + private async Task OnChange() + { + RebuildVariables(); + await InvokeAsync(StateHasChanged); + } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Websites/New.razor b/Moonlight/Shared/Views/Admin/Websites/New.razor index 4dcc6cfe..e4206aa5 100644 --- a/Moonlight/Shared/Views/Admin/Websites/New.razor +++ b/Moonlight/Shared/Views/Admin/Websites/New.razor @@ -2,7 +2,6 @@ @using Moonlight.App.Models.Forms @using Moonlight.App.Services -@using Blazored.Typeahead @using Moonlight.App.Database.Entities @using Moonlight.App.Repositories @@ -24,15 +23,11 @@ Owner
- - - @(context.Email) - - - @(context.Email) - - + +
+
+ @code { - [CascadingParameter] - public User User { get; set; } - - private FileAccess FileAccess; + private User[] Users; + private TestDataModel Model = new(); private Task Load(LazyLoader arg) { - FileAccess = new FtpFileAccess("vps01.so.host.endelon.link", 21, "example.com", "61P8JZzfjSNyhtZl"); - + Users = UserRepository.Get().ToArray(); + return Task.CompletedTask; } -} \ No newline at end of file + + private Task OnValidSubmit() + { + return Task.CompletedTask; + } +} diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index c0ba8c4f..a8d79100 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -513,3 +513,15 @@ Error from plesk;Error from plesk Host;Host Username;Username SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one +The User field is required.;The User field is required. +You need to specify a owner;You need to specify a owner +You need to specify a image;You need to specify a image +Api Url;Api Url +Api Key;Api Key +Duration;Duration +Enter duration of subscription;Enter duration of subscription +Copied code to clipboard;Copied code to clipboard +Invalid or expired subscription code;Invalid or expired subscription code +Current subscription;Current subscription +You need to specify a server image;You need to specify a server image +CPU;CPU From ed7f5e9ca1979e3568e54deb399444dc3e6793fc Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 11 Apr 2023 00:12:14 +0200 Subject: [PATCH 18/45] Implemented website order --- .../App/Models/Forms/WebsiteDataModel.cs | 2 +- .../App/Models/Forms/WebsiteOrderDataModel.cs | 10 ++ Moonlight/App/Services/SmartDeployService.cs | 23 ++- Moonlight/Shared/Views/Servers/Create.razor | 12 +- Moonlight/Shared/Views/Websites/Create.razor | 138 ++++++++++++++++++ Moonlight/Shared/Views/Websites/New.razor | 1 - Moonlight/resources/lang/de_de.lang | 4 + 7 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs create mode 100644 Moonlight/Shared/Views/Websites/Create.razor delete mode 100644 Moonlight/Shared/Views/Websites/New.razor diff --git a/Moonlight/App/Models/Forms/WebsiteDataModel.cs b/Moonlight/App/Models/Forms/WebsiteDataModel.cs index d4a7f4bf..9bc9d2df 100644 --- a/Moonlight/App/Models/Forms/WebsiteDataModel.cs +++ b/Moonlight/App/Models/Forms/WebsiteDataModel.cs @@ -4,7 +4,7 @@ namespace Moonlight.App.Models.Forms; public class WebsiteDataModel { - [Required(ErrorMessage = "You need a domain")] + [Required(ErrorMessage = "You need to enter a domain")] [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] public string BaseDomain { get; set; } = ""; } \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs b/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs new file mode 100644 index 00000000..0c5a388e --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteOrderDataModel +{ + [Required(ErrorMessage = "You need to enter a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs index 0585c1dd..57aa4bc8 100644 --- a/Moonlight/App/Services/SmartDeployService.cs +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -6,12 +6,18 @@ namespace Moonlight.App.Services; public class SmartDeployService { private readonly NodeRepository NodeRepository; + private readonly PleskServerRepository PleskServerRepository; + private readonly WebsiteService WebsiteService; private readonly NodeService NodeService; - public SmartDeployService(NodeRepository nodeRepository, NodeService nodeService) + public SmartDeployService( + NodeRepository nodeRepository, + NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService) { NodeRepository = nodeRepository; NodeService = nodeService; + PleskServerRepository = pleskServerRepository; + WebsiteService = websiteService; } public async Task GetNode() @@ -32,6 +38,21 @@ public class SmartDeployService return data.MaxBy(x => x.Value).Key; } + public async Task GetPleskServer() + { + var result = new List(); + + foreach (var pleskServer in PleskServerRepository.Get().ToArray()) + { + if (await WebsiteService.IsHostUp(pleskServer)) + { + result.Add(pleskServer); + } + } + + return result.FirstOrDefault(); + } + private async Task GetUsageScore(Node node) { var score = 0; diff --git a/Moonlight/Shared/Views/Servers/Create.razor b/Moonlight/Shared/Views/Servers/Create.razor index de782e1d..bc372c1c 100644 --- a/Moonlight/Shared/Views/Servers/Create.razor +++ b/Moonlight/Shared/Views/Servers/Create.razor @@ -27,7 +27,7 @@ No node found

- No node found to deploy to found + No node found to deploy to

@@ -36,8 +36,8 @@ else {
-
-
+
+

@@ -48,7 +48,7 @@
- +
@(DeployNode.Name)
@if (Model.Image != null) @@ -159,11 +159,9 @@ Subscription = await SubscriptionService.GetCurrent(); await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy node")); - DeployNode = await SmartDeployService.GetNode(); await lazyLoader.SetText(SmartTranslateService.Translate("Searching for available images")); - var images = ImageRepository.Get().ToArray(); foreach (var image in images) @@ -179,7 +177,7 @@ .Where(x => x.Owner.Id == User.Id) .Count(x => x.Image.Id == image.Id); - if(serversCount <= limit.Amount) //TODO: FIX COUNTING + if(serversCount < limit.Amount) Images.Add(image, limit); } } diff --git a/Moonlight/Shared/Views/Websites/Create.razor b/Moonlight/Shared/Views/Websites/Create.razor new file mode 100644 index 00000000..0f936421 --- /dev/null +++ b/Moonlight/Shared/Views/Websites/Create.razor @@ -0,0 +1,138 @@ +@page "/websites/create" +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms +@using Moonlight.App.Repositories + +@inject SubscriptionService SubscriptionService +@inject WebsiteService WebsiteService +@inject WebsiteRepository WebsiteRepository +@inject SmartDeployService SmartDeployService +@inject SmartTranslateService SmartTranslateService +@inject NavigationManager NavigationManager + + + @if (PleskServer == null) + { +
+
+ Not found image +
+

+ No plesk server found +

+

+ No plesk server found to deploy to +

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

+ Website details +

+
+
+
+
+
+ +
@(PleskServer.Name)
+
+ @if (AllowOrder) + { +
+ +
@(Model.BaseDomain)
+
+ } +
+
+
+
+
+
+
+
+

+ Configure your website +

+
+
+
+ + @if (AllowOrder) + { + +
+ +
+ + + } + else + { +
+ + You reached the maximum amount of websites in your subscription: @(Subscription == null ? SmartTranslateService.Translate("Default") : Subscription.Name) + +
+ } +
+
+
+
+
+ } +
+ +@code +{ + [CascadingParameter] + public User User { get; set; } + + private Subscription? Subscription; + private PleskServer? PleskServer; + private bool AllowOrder = false; + + private WebsiteOrderDataModel Model = new(); + + private async Task Load(LazyLoader lazyLoader) + { + // Reset state + Model = new(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Loading your subscription")); + Subscription = await SubscriptionService.GetCurrent(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy plesk server")); + PleskServer = await SmartDeployService.GetPleskServer(); + + AllowOrder = WebsiteRepository.Get().Count() < (await SubscriptionService.GetLimit("websites")).Amount; + } + + private async Task OnValidSubmit() + { + if (WebsiteRepository.Get().Count() < (await SubscriptionService.GetLimit("websites")).Amount) + { + var website = await WebsiteService.Create(Model.BaseDomain, User, PleskServer); + + NavigationManager.NavigateTo($"/website/{website.Id}"); + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Websites/New.razor b/Moonlight/Shared/Views/Websites/New.razor deleted file mode 100644 index 84255d65..00000000 --- a/Moonlight/Shared/Views/Websites/New.razor +++ /dev/null @@ -1 +0,0 @@ -@page "/websites/new" \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index f0feb020..84e4d1db 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -531,3 +531,7 @@ Month;Month Year;Year All time;All time This function is not implemented;This function is not implemented +Searching for deploy plesk server;Searching for deploy plesk server +Website details;Website details +Configure your website;Configure your website +You reached the maximum amount of websites in your subscription;You reached the maximum amount of websites in your subscription From bc3c59e20c8a224f3646b22d87c8e1ecb31bdf0f Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 11 Apr 2023 17:55:08 +0200 Subject: [PATCH 19/45] Update langs --- .idea/vcs.xml | 6 ++++++ Moonlight/resources/lang/de_de.lang | 1 + 2 files changed, 7 insertions(+) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index f0feb020..e27bf1c1 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -531,3 +531,4 @@ Month;Month Year;Year All time;All time This function is not implemented;This function is not implemented +Backup download successfully started;Backup download successfully started From 3189f901a833888cd3cfc1e3d4367816d6eae756 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 11 Apr 2023 21:36:25 +0200 Subject: [PATCH 20/45] Implemented domain order. Fixed some bugs --- .../App/Models/Forms/DomainOrderDataModel.cs | 15 ++ Moonlight/App/Services/DomainService.cs | 22 +++ .../Shared/Views/Admin/Domains/Index.razor | 14 +- .../Shared/Views/Admin/Domains/New.razor | 11 +- Moonlight/Shared/Views/Domains/Create.razor | 156 ++++++++++++++++++ .../{Domains.razor => Domains/Index.razor} | 0 Moonlight/Shared/Views/Websites/Create.razor | 6 +- Moonlight/resources/lang/de_de.lang | 5 + Moonlight/resources/lang/en_us.lang | 64 +++++++ 9 files changed, 278 insertions(+), 15 deletions(-) create mode 100644 Moonlight/App/Models/Forms/DomainOrderDataModel.cs create mode 100644 Moonlight/Shared/Views/Domains/Create.razor rename Moonlight/Shared/Views/{Domains.razor => Domains/Index.razor} (100%) diff --git a/Moonlight/App/Models/Forms/DomainOrderDataModel.cs b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs new file mode 100644 index 00000000..c49dd8e3 --- /dev/null +++ b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +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")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a shared domain")] + public SharedDomain SharedDomain { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DomainService.cs b/Moonlight/App/Services/DomainService.cs index 4bdbf9b3..1b2cb98c 100644 --- a/Moonlight/App/Services/DomainService.cs +++ b/Moonlight/App/Services/DomainService.cs @@ -48,6 +48,28 @@ public class DomainService ); } + public Task Create(string domain, SharedDomain sharedDomain, User user) + { + if (DomainRepository.Get().Where(x => x.SharedDomain.Id == sharedDomain.Id).Any(x => x.Name == domain)) + throw new DisplayException("A domain with this name does already exist for this shared domain"); + + var res = DomainRepository.Add(new() + { + Name = domain, + SharedDomain = sharedDomain, + Owner = user + }); + + return Task.FromResult(res); + } + + public Task Delete(Domain domain) + { + DomainRepository.Delete(domain); + + return Task.CompletedTask; + } + public async Task GetAvailableDomains() // This method returns all available domains which are not added as a shared domain { diff --git a/Moonlight/Shared/Views/Admin/Domains/Index.razor b/Moonlight/Shared/Views/Admin/Domains/Index.razor index 0f5cfc48..09c0d307 100644 --- a/Moonlight/Shared/Views/Admin/Domains/Index.razor +++ b/Moonlight/Shared/Views/Admin/Domains/Index.razor @@ -6,10 +6,11 @@ @using Moonlight.App.Services @inject DomainRepository DomainRepository +@inject DomainService DomainService @inject SmartTranslateService SmartTranslateService - +
@@ -47,11 +48,9 @@ @@ -65,6 +64,7 @@ @code { private Domain[] Domains; + private LazyLoader LazyLoader; private Task Load(LazyLoader arg) { @@ -79,5 +79,7 @@ private async Task Delete(Domain context) { + await DomainService.Delete(context); + await LazyLoader.Reload(); } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Domains/New.razor b/Moonlight/Shared/Views/Admin/Domains/New.razor index 203456ad..0b08c4c0 100644 --- a/Moonlight/Shared/Views/Admin/Domains/New.razor +++ b/Moonlight/Shared/Views/Admin/Domains/New.razor @@ -4,13 +4,12 @@ @using Moonlight.App.Models.Forms @using Moonlight.App.Repositories @using Moonlight.App.Repositories.Domains -@using Mappy.Net @inject SmartTranslateService SmartTranslateService @inject SharedDomainRepository SharedDomainRepository -@inject DomainRepository DomainRepository @inject UserRepository UserRepository @inject NavigationManager NavigationManager +@inject DomainService DomainService
@@ -66,14 +65,10 @@ return Task.CompletedTask; } - private Task Add() + private async Task Add() { - var domain = Mapper.Map(Model); - - DomainRepository.Add(domain); + await DomainService.Create(Model.Name, Model.SharedDomain, Model.Owner); NavigationManager.NavigateTo("/admin/domains"); - - return Task.CompletedTask; } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Domains/Create.razor b/Moonlight/Shared/Views/Domains/Create.razor new file mode 100644 index 00000000..d731acda --- /dev/null +++ b/Moonlight/Shared/Views/Domains/Create.razor @@ -0,0 +1,156 @@ +@page "/domains/create" +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms +@using Moonlight.App.Repositories.Domains +@using Microsoft.EntityFrameworkCore + +@inject SubscriptionService SubscriptionService +@inject DomainService DomainService +@inject DomainRepository DomainRepository +@inject SharedDomainRepository SharedDomainRepository +@inject NavigationManager NavigationManager +@inject SmartTranslateService SmartTranslateService + + + @if (!SharedDomains.Any()) + { +
+
+ Not found image +
+

+ No shared domain found +

+

+ No shared domain found +

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

+ Domain details +

+
+
+
+
+ @if (AllowOrder) + { +
+ +
@(Model.Name)
+
+
+ +
@(Model.SharedDomain == null ? "" : Model.SharedDomain.Name)
+
+ } +
+
+
+
+
+
+
+
+

+ Configure your domain +

+
+
+
+ + @if (AllowOrder) + { + +
+ +
+ +
+ + + +
+ + + } + else + { +
+ + You reached the maximum amount of domains in your subscription: @(Subscription == null ? SmartTranslateService.Translate("Default") : Subscription.Name) + +
+ } +
+
+
+
+
+ } +
+ +@code +{ + [CascadingParameter] + public User User { get; set; } + + private SharedDomain[] SharedDomains; + + private Subscription? Subscription; + + private bool AllowOrder = false; + + private DomainOrderDataModel Model = new(); + + private async Task Load(LazyLoader lazyLoader) + { + Model = new(); + + await lazyLoader.SetText(SmartTranslateService.Translate("Loading your subscription")); + Subscription = await SubscriptionService.GetCurrent(); + + AllowOrder = DomainRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("domains")).Amount; + + await lazyLoader.SetText("Loading shared domains"); + SharedDomains = SharedDomainRepository.Get().ToArray(); + } + + private async Task OnValidSubmit() + { + if (DomainRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("domains")).Amount) + { + var domain = await DomainService.Create(Model.Name, Model.SharedDomain, User); + + NavigationManager.NavigateTo($"/domain/{domain.Id}"); + } + } +} diff --git a/Moonlight/Shared/Views/Domains.razor b/Moonlight/Shared/Views/Domains/Index.razor similarity index 100% rename from Moonlight/Shared/Views/Domains.razor rename to Moonlight/Shared/Views/Domains/Index.razor diff --git a/Moonlight/Shared/Views/Websites/Create.razor b/Moonlight/Shared/Views/Websites/Create.razor index 0f936421..5533cd19 100644 --- a/Moonlight/Shared/Views/Websites/Create.razor +++ b/Moonlight/Shared/Views/Websites/Create.razor @@ -3,6 +3,7 @@ @using Moonlight.App.Database.Entities @using Moonlight.App.Models.Forms @using Moonlight.App.Repositories +@using Microsoft.EntityFrameworkCore @inject SubscriptionService SubscriptionService @inject WebsiteService WebsiteService @@ -123,7 +124,10 @@ await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy plesk server")); PleskServer = await SmartDeployService.GetPleskServer(); - AllowOrder = WebsiteRepository.Get().Count() < (await SubscriptionService.GetLimit("websites")).Amount; + AllowOrder = WebsiteRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount; } private async Task OnValidSubmit() diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index f0feb020..1b1909d6 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -531,3 +531,8 @@ Month;Month Year;Year All time;All time This function is not implemented;This function is not implemented +Domain details;Domain details +Configure your domain;Configure your domain +You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription +You need to specify a shared domain;You need to specify a shared domain +A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/resources/lang/en_us.lang index 75bac759..88f53b6f 100644 --- a/Moonlight/resources/lang/en_us.lang +++ b/Moonlight/resources/lang/en_us.lang @@ -19,3 +19,67 @@ Forgot password?;Forgot password? Sign-in;Sign-in Not registered yet?;Not registered yet? Sign up;Sign up +Profile;Profile +Logout;Logout +Dashboard;Dashboard +Servers;Servers +Websites;Websites +Domains;Domains +Changelog;Changelog +Admin;Admin +System;System +Overview;Overview +Manager;Manager +Cleanup;Cleanup +Nodes;Nodes +Images;Images +Users;Users +Shared domains;Shared domains +Support;Support +Subscriptions;Subscriptions +Statistics;Statistics +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records +New server;New server +Id;Id +Name;Name +Cores;Cores +Memory;Memory +Disk;Disk +Owner;Owner +Manage;Manage +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Sessions;Sessions +New user;New user +First name;First name +Last name;Last name +Created at;Created at +Refresh;Refresh +Send a message to all users;Send a message to all users +IP;IP +URL;URL +Device;Device +Time;Time +Actions;Actions +Change url;Change url +Message;Message +Enter url;Enter url +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you +less than a minute ago;less than a minute ago +The support team has been notified. Please be patient;The support team has been notified. Please be patient +is typing;is typing From 8de2aa1bcaccf6f742314f0300a6c8dcd3f9e27f Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 16:42:40 +0200 Subject: [PATCH 21/45] Update langs --- Moonlight/resources/lang/de_de.lang | 10 ++++++++++ Moonlight/resources/lang/en_us.lang | 1 + 2 files changed, 11 insertions(+) diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 1b1909d6..95a82935 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -536,3 +536,13 @@ Configure your domain;Configure your domain You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription You need to specify a shared domain;You need to specify a shared domain A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain +Cleanup exception;Cleanup exception +No shared domain found;No shared domain found +Searching for deploy plesk server;Searching for deploy plesk server +No plesk server found;No plesk server found +No plesk server found to deploy to;No plesk server found to deploy to +No node found to deploy to;No node found to deploy to +Website details;Website details +Configure your website;Configure your website +The name cannot be longer that 32 characters;The name cannot be longer that 32 characters +The name should only consist of lower case characters;The name should only consist of lower case characters diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/resources/lang/en_us.lang index 88f53b6f..2e2e1fcb 100644 --- a/Moonlight/resources/lang/en_us.lang +++ b/Moonlight/resources/lang/en_us.lang @@ -83,3 +83,4 @@ Welcome to the support chat. Ask your question here and we will help you;Welcome less than a minute ago;less than a minute ago The support team has been notified. Please be patient;The support team has been notified. Please be patient is typing;is typing +Proccessing;Proccessing From c3d7f7e36e62c51f7c3ac555e4903bde859bdff7 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 16:52:09 +0200 Subject: [PATCH 22/45] Implemented new soft error boundary crash handler --- .../ErrorBoundaries/SoftErrorBoundary.razor | 29 ++++++- Moonlight/Shared/Layouts/MainLayout.razor | 82 +++++++++---------- Moonlight/Shared/Views/Test.razor | 29 +------ 3 files changed, 67 insertions(+), 73 deletions(-) diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index 7f561201..3d59dd7c 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -7,13 +7,35 @@ @inject AlertService AlertService @inject SmartTranslateService SmartTranslateService -@ChildContent +@if (Crashed) +{ +
+
+
+
+ + Ooops. This page is crashed + +
+
+ This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page +
+
+
+
+} +else +{ + @ChildContent +} @code { + private bool Crashed = false; + protected override async Task OnErrorAsync(Exception exception) { - Logger.Debug(exception); + Logger.Warn(exception); if (exception is DisplayException displayException) { @@ -49,7 +71,8 @@ } else { - throw exception; + Crashed = true; + await InvokeAsync(StateHasChanged); } } } \ No newline at end of file diff --git a/Moonlight/Shared/Layouts/MainLayout.razor b/Moonlight/Shared/Layouts/MainLayout.razor index 4c42ecb7..46017b54 100644 --- a/Moonlight/Shared/Layouts/MainLayout.razor +++ b/Moonlight/Shared/Layouts/MainLayout.razor @@ -59,57 +59,55 @@
- - - @if (uri.LocalPath != "/login" && - uri.LocalPath != "/passwordreset" && - uri.LocalPath != "/register") + + @if (uri.LocalPath != "/login" && + uri.LocalPath != "/passwordreset" && + uri.LocalPath != "/register") + { + if (User == null) { - if (User == null) - { - - } - else - { - if (User.Status == UserStatus.Banned) - { - - } - else if (User.Status == UserStatus.Disabled) - { - - } - else if (User.Status == UserStatus.PasswordPending) - { - - } - else if (User.Status == UserStatus.DataPending) - { - - } - else - { - @Body - } - } + } else { - if (uri.LocalPath == "/login") + if (User.Status == UserStatus.Banned) { - + } - else if (uri.LocalPath == "/register") + else if (User.Status == UserStatus.Disabled) { - + } - else if (uri.LocalPath == "/passwordreset") + else if (User.Status == UserStatus.PasswordPending) { - + + } + else if (User.Status == UserStatus.DataPending) + { + + } + else + { + @Body } } - - + } + else + { + if (uri.LocalPath == "/login") + { + + } + else if (uri.LocalPath == "/register") + { + + } + else if (uri.LocalPath == "/passwordreset") + { + + } + } +
@@ -161,8 +159,8 @@ await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-page-loading"); await JsRuntime.InvokeVoidAsync("KTMenu.createInstances"); await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances"); - - //await JsRuntime.InvokeVoidAsync("createSnow"); + + //await JsRuntime.InvokeVoidAsync("createSnow"); await SessionService.Register(); diff --git a/Moonlight/Shared/Views/Test.razor b/Moonlight/Shared/Views/Test.razor index 88c12f01..906f4241 100644 --- a/Moonlight/Shared/Views/Test.razor +++ b/Moonlight/Shared/Views/Test.razor @@ -1,39 +1,12 @@ @page "/test" -@using Moonlight.App.Repositories -@using Moonlight.App.Database.Entities -@using Moonlight.App.Models.Forms - -@inject UserRepository UserRepository - -
- -
-
- -
-
@code { - private User[] Users; - private TestDataModel Model = new(); - private Task Load(LazyLoader arg) { - Users = UserRepository.Get().ToArray(); - - return Task.CompletedTask; - } - - private Task OnValidSubmit() - { - return Task.CompletedTask; + throw new Exception("Nein"); } } From ba373b86ee4a0987665f377f8be661f8bc54c899 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 17:06:29 +0200 Subject: [PATCH 23/45] Added ip locate. Fixed oauth2 mail send. Fixed mail send when generating tokens --- .../Api/Moonlight/OAuth2Controller.cs | 4 +-- Moonlight/App/Services/UserService.cs | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs index 7452ce49..3d1abb4c 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs @@ -58,7 +58,7 @@ public class OAuth2Controller : Controller } else { - token = await UserService.GenerateToken(user); + token = await UserService.GenerateToken(user, true); } Response.Cookies.Append("token", token, new () @@ -116,7 +116,7 @@ public class OAuth2Controller : Controller } else { - token = await UserService.GenerateToken(user); + token = await UserService.GenerateToken(user, true); } Response.Cookies.Append("token", token, new () diff --git a/Moonlight/App/Services/UserService.cs b/Moonlight/App/Services/UserService.cs index 9cf6aa77..6a6fe022 100644 --- a/Moonlight/App/Services/UserService.cs +++ b/Moonlight/App/Services/UserService.cs @@ -18,6 +18,7 @@ public class UserService private readonly AuditLogService AuditLogService; private readonly MailService MailService; private readonly IdentityService IdentityService; + private readonly IpLocateService IpLocateService; private readonly string JwtSecret; @@ -28,7 +29,7 @@ public class UserService SecurityLogService securityLogService, AuditLogService auditLogService, MailService mailService, - IdentityService identityService) + IdentityService identityService, IpLocateService ipLocateService) { UserRepository = userRepository; TotpService = totpService; @@ -36,6 +37,7 @@ public class UserService AuditLogService = auditLogService; MailService = mailService; IdentityService = identityService; + IpLocateService = ipLocateService; JwtSecret = configService .GetSection("Moonlight") @@ -77,6 +79,7 @@ public class UserService }); await MailService.SendMail(user!, "register", values => {}); + await AuditLogService.Log(AuditLogType.Register, x => { x.Add(user.Email); @@ -177,11 +180,13 @@ public class UserService } else { + var location = await IpLocateService.GetLocation(); + await MailService.SendMail(user!, "passwordChange", values => { values.Add("Ip", IdentityService.GetIp()); values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); + values.Add("Location", location); }); await AuditLogService.Log(AuditLogType.ChangePassword, x => @@ -201,6 +206,7 @@ public class UserService { x.Add(id); }); + throw new Exception("Invalid username"); } @@ -223,12 +229,17 @@ public class UserService public async Task GenerateToken(User user, bool sendMail = false) { - await MailService.SendMail(user!, "login", values => + var location = await IpLocateService.GetLocation(); + + if (sendMail) { - values.Add("Ip", IdentityService.GetIp()); - values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); - }); + await MailService.SendMail(user!, "login", values => + { + values.Add("Ip", IdentityService.GetIp()); + values.Add("Device", IdentityService.GetDevice()); + values.Add("Location", location); + }); + } var token = JwtBuilder.Create() .WithAlgorithm(new HMACSHA256Algorithm()) @@ -257,11 +268,13 @@ public class UserService await AuditLogService.Log(AuditLogType.PasswordReset, x => {}); + var location = await IpLocateService.GetLocation(); + await MailService.SendMail(user, "passwordReset", values => { values.Add("Ip", IdentityService.GetIp()); values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); + values.Add("Location", location); values.Add("Password", newPassword); }); } From cf287173b807d8f0268a841f93fdac81d92dc921 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 17:42:24 +0200 Subject: [PATCH 24/45] Implemented multi allocation --- Moonlight/App/Services/ServerService.cs | 52 ++++++++++----------- Moonlight/Shared/Views/Servers/Create.razor | 4 +- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index bc22343d..ccde6026 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -195,7 +195,7 @@ public class ServerService ServerRepository.Update(serverData); await MessageService.Emit("wings.backups.delete", backup); - + await AuditLogService.Log(AuditLogType.DeleteBackup, x => { @@ -213,7 +213,7 @@ public class ServerService claims.Add("server_uuid", server.Uuid.ToString()); claims.Add("backup_uuid", serverBackup.Uuid.ToString()); }); - + await AuditLogService.Log(AuditLogType.DownloadBackup, x => { @@ -221,7 +221,10 @@ public class ServerService x.Add(serverBackup.Uuid); }); - return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; + if (server.Node.Ssl) + return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; + else + return $"http://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; } public Task CreateFileAccess(Server s, User user) // We need the user to create the launch url @@ -240,7 +243,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) + Action? modifyDetails = null, int allocations = 1) { var user = UserRepository .Get() @@ -264,22 +267,27 @@ public class ServerService else node = n; - NodeAllocation freeAllo; + NodeAllocation[] freeAllocations; try { - freeAllo = node.Allocations.First(a => !ServerRepository.Get() - .SelectMany(s => s.Allocations) - .Any(b => b.Id == a.Id)); // Thank you ChatGPT <3 + freeAllocations = node.Allocations + .Where(a => !ServerRepository.Get() + .SelectMany(s => s.Allocations) + .Any(b => b.Id == a.Id)) + .Take(allocations).ToArray(); } catch (Exception) { throw new DisplayException("No allocation found"); } - if (freeAllo == null) + if (!freeAllocations.Any()) throw new DisplayException("No allocation found"); + if (freeAllocations.Length != allocations) + throw new DisplayException("Not enough allocations found"); + var server = new Server() { Cpu = cpu, @@ -290,11 +298,8 @@ public class ServerService Owner = user, Node = node, Uuid = Guid.NewGuid(), - MainAllocation = freeAllo, - Allocations = new() - { - freeAllo - }, + MainAllocation = freeAllocations.First(), + Allocations = freeAllocations.ToList(), Backups = new(), OverrideStartup = "", DockerImageIndex = image.DockerImages.FindIndex(x => x.Default) @@ -322,10 +327,7 @@ public class ServerService StartOnCompletion = false }); - await AuditLogService.Log(AuditLogType.CreateServer, x => - { - x.Add(newServerData.Uuid); - }); + await AuditLogService.Log(AuditLogType.CreateServer, x => { x.Add(newServerData.Uuid); }); return newServerData; } @@ -333,7 +335,7 @@ public class ServerService { await ErrorLogService.Log(e, x => { - x.Add(newServerData.Uuid); + x.Add(newServerData.Uuid); x.Add(node.Id); }); @@ -349,10 +351,7 @@ public class ServerService await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null); - await AuditLogService.Log(AuditLogType.ReinstallServer, x => - { - x.Add(server.Uuid); - }); + await AuditLogService.Log(AuditLogType.ReinstallServer, x => { x.Add(server.Uuid); }); } public async Task SftpServerLogin(int serverId, int id, string password) @@ -361,10 +360,7 @@ public class ServerService if (server == null) { - await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => - { - x.Add(id); - }); + await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => { x.Add(id); }); throw new Exception("Server not found"); } @@ -393,7 +389,7 @@ public class ServerService var server = EnsureNodeData(s); await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null); - + ServerRepository.Delete(s); } diff --git a/Moonlight/Shared/Views/Servers/Create.razor b/Moonlight/Shared/Views/Servers/Create.razor index bc372c1c..b6c6c318 100644 --- a/Moonlight/Shared/Views/Servers/Create.razor +++ b/Moonlight/Shared/Views/Servers/Create.razor @@ -209,7 +209,9 @@ disk, User, Model.Image, - DeployNode + DeployNode, + null, + Model.Image.Allocations ); NavigationManager.NavigateTo($"/server/{server.Uuid}"); From ecaab3a755a0b0dee77011886a45a772e1e82aeb Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 12 Apr 2023 18:01:18 +0200 Subject: [PATCH 25/45] register --- Moonlight/App/Models/Forms/UserDataModel.cs | 27 +++++++ .../App/Models/Forms/UserRegisterModel.cs | 21 +++++ .../Shared/Components/Auth/Register.razor | 81 +++++++++++++------ Moonlight/Shared/Views/Profile/Index.razor | 23 +++++- Moonlight/resources/lang/de_de.lang | 4 + 5 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 Moonlight/App/Models/Forms/UserDataModel.cs create mode 100644 Moonlight/App/Models/Forms/UserRegisterModel.cs diff --git a/Moonlight/App/Models/Forms/UserDataModel.cs b/Moonlight/App/Models/Forms/UserDataModel.cs new file mode 100644 index 00000000..bc2ff8ef --- /dev/null +++ b/Moonlight/App/Models/Forms/UserDataModel.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class UserDataModel +{ + [Required] + public string FirstName { get; set; } = ""; + + [Required] + public string LastName { get; set; } = ""; + + [Required] + public string Email { get; set; } = ""; + + [Required] + public string Address { get; set; } = ""; + + [Required] + public string City { get; set; } = ""; + + [Required] + public string State { get; set; } = ""; + + [Required] + public string Country { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/UserRegisterModel.cs b/Moonlight/App/Models/Forms/UserRegisterModel.cs new file mode 100644 index 00000000..399ddb7d --- /dev/null +++ b/Moonlight/App/Models/Forms/UserRegisterModel.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class UserRegisterModel +{ + [Required, EmailAddress] + public string Email { get; set; } + + [Required, MinLength(3)] + public string FirstName { get; set; } + + [Required, MinLength(3)] + public string LastName { get; set; } + + [Required] + public string Password { get; set; } + + [Required] + public string ConfirmPassword { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Auth/Register.razor b/Moonlight/Shared/Components/Auth/Register.razor index ab8010b8..0b284a2d 100644 --- a/Moonlight/Shared/Components/Auth/Register.razor +++ b/Moonlight/Shared/Components/Auth/Register.razor @@ -7,11 +7,17 @@ @using Moonlight.App.Services @using Moonlight.App.Services.OAuth2 +@using Moonlight.App.Models.Forms +@using Moonlight.App.Services.Interop +@using Moonlight.App.Services.Sessions @inject SmartTranslateService SmartTranslateService @inject GoogleOAuth2Service GoogleOAuth2Service @inject NavigationManager NavigationManager @inject DiscordOAuth2Service DiscordOAuth2Service +@inject AlertService AlertService +@inject UserService UserService +@inject CookieService CookieService
@@ -46,38 +52,44 @@
-
+
Or with email
+ + +
+ +
+ +
+
+ +
-
- -
+
+ +
+
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
+
+
+ +
+
+ +
+
+ +
+ +
+
+
Already registered? @@ -93,6 +105,8 @@ @code { + private UserRegisterModel UserRegisterModel = new(); + private async Task DoGoogle() { var url = await GoogleOAuth2Service.GetUrl(); @@ -104,4 +118,21 @@ var url = await DiscordOAuth2Service.GetUrl(); NavigationManager.NavigateTo(url, true); } + + private async Task CreateUser() + { + if (UserRegisterModel.ConfirmPassword != UserRegisterModel.Password) + { + await AlertService.Error(SmartTranslateService.Translate("Passwords need to match")); + return; + } + + var token = await UserService.Register(UserRegisterModel.Email, UserRegisterModel.Password, UserRegisterModel.FirstName, UserRegisterModel.LastName); + await CookieService.SetValue("token", token, 10); + + if (NavigationManager.Uri.EndsWith("register")) + NavigationManager.NavigateTo("/", true); + else + NavigationManager.NavigateTo(NavigationManager.Uri, true); + } } diff --git a/Moonlight/Shared/Views/Profile/Index.razor b/Moonlight/Shared/Views/Profile/Index.razor index cb1451ce..ea552ff4 100644 --- a/Moonlight/Shared/Views/Profile/Index.razor +++ b/Moonlight/Shared/Views/Profile/Index.razor @@ -3,7 +3,9 @@ @using Moonlight.Shared.Components.Navigations @using Moonlight.App.Services.Sessions @using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms @using Moonlight.App.Repositories +@using Moonlight.Shared.Components.Auth @inject IdentityService IdentityService @inject UserRepository UserRepository @@ -74,17 +76,32 @@ @code { - private User User = new User(); + private UserDataModel User = new UserDataModel(); + private User CurrentUser; private async Task Load(LazyLoader loader) { - User = await IdentityService.Get(); + CurrentUser = await IdentityService.Get(); + User.FirstName = CurrentUser.FirstName; + User.LastName = CurrentUser.LastName; + User.Email = CurrentUser.Email; + User.Address = CurrentUser.Address; + User.City = CurrentUser.City; + User.State = CurrentUser.State; + User.Country = CurrentUser.Country; } private Task Save() { - UserRepository.Update(User); + CurrentUser.FirstName = User.FirstName; + CurrentUser.LastName = User.LastName; + CurrentUser.Email = User.Email; + CurrentUser.Address = User.Address; + CurrentUser.City = User.City; + CurrentUser.State = User.State; + CurrentUser.Country = User.Country; + UserRepository.Update(CurrentUser); return Task.CompletedTask; } diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 1b1909d6..47d35db4 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -536,3 +536,7 @@ Configure your domain;Configure your domain You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription You need to specify a shared domain;You need to specify a shared domain A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain +The Email field is required.;The Email field is required. +The Password field is required.;The Password field is required. +The ConfirmPassword field is required.;The ConfirmPassword field is required. +Passwords need to match;Passwords need to match From e196326d9e42d079b5a97646161073ded30e1e34 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 12 Apr 2023 18:28:25 +0200 Subject: [PATCH 26/45] added table + repository --- Moonlight/App/Database/DataContext.cs | 1 + Moonlight/App/Database/Entities/NewsEntry.cs | 10 + ...12162710_NewsEntriesTableAdded.Designer.cs | 997 ++++++++++++++++++ .../20230412162710_NewsEntriesTableAdded.cs | 57 + .../Migrations/DataContextModelSnapshot.cs | 26 +- .../App/Repositories/NewsEntryRepository.cs | 44 + Moonlight/Program.cs | 1 + 7 files changed, 1134 insertions(+), 2 deletions(-) create mode 100644 Moonlight/App/Database/Entities/NewsEntry.cs create mode 100644 Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs create mode 100644 Moonlight/App/Repositories/NewsEntryRepository.cs diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 2cfb7d9e..4b7be797 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -42,6 +42,7 @@ public class DataContext : DbContext public DbSet PleskServers { get; set; } public DbSet Websites { get; set; } public DbSet Statistics { get; set; } + public DbSet NewsEntries { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/NewsEntry.cs b/Moonlight/App/Database/Entities/NewsEntry.cs new file mode 100644 index 00000000..d40fb26c --- /dev/null +++ b/Moonlight/App/Database/Entities/NewsEntry.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Database.Entities; + +public class NewsEntry +{ + public int Id { get; set; } + + public DateTime Date { get; set; } + public string Title { get; set; } + public string Markdown { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs new file mode 100644 index 00000000..bf04412e --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs @@ -0,0 +1,997 @@ +// +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("20230412162710_NewsEntriesTableAdded")] + partial class NewsEntriesTableAdded + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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.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.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") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + 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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs new file mode 100644 index 00000000..b35646ae --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class NewsEntriesTableAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DiscordId", + table: "Users", + type: "bigint unsigned", + nullable: false, + oldClrType: typeof(long), + oldType: "bigint"); + + migrationBuilder.CreateTable( + name: "NewsEntries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Date = table.Column(type: "datetime(6)", nullable: false), + Title = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Markdown = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_NewsEntries", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "NewsEntries"); + + migrationBuilder.AlterColumn( + name: "DiscordId", + table: "Users", + type: "bigint", + nullable: false, + oldClrType: typeof(ulong), + oldType: "bigint unsigned"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 96bc5cb9..dbe9e502 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -296,6 +296,28 @@ namespace Moonlight.App.Database.Migrations b.ToTable("SecurityLog"); }); + 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") @@ -686,8 +708,8 @@ namespace Moonlight.App.Database.Migrations b.Property("CurrentSubscriptionId") .HasColumnType("int"); - b.Property("DiscordId") - .HasColumnType("bigint"); + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); b.Property("Email") .IsRequired() diff --git a/Moonlight/App/Repositories/NewsEntryRepository.cs b/Moonlight/App/Repositories/NewsEntryRepository.cs new file mode 100644 index 00000000..edd01181 --- /dev/null +++ b/Moonlight/App/Repositories/NewsEntryRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class NewsEntryRepository +{ + private readonly DataContext DataContext; + + public NewsEntryRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.NewsEntries; + } + + public NewsEntry Add(NewsEntry newsEntry) + { + var x = DataContext.NewsEntries.Add(newsEntry); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(NewsEntry newsEntry) + { + DataContext.NewsEntries.Update(newsEntry); + DataContext.SaveChanges(); + } + + public void Delete(NewsEntry newsEntry) + { + DataContext.NewsEntries.Remove(newsEntry); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 1a0b45d4..8651fe58 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -64,6 +64,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); From 5438bfb79cbe3114edbaeff577474ce535bd4904 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 19:06:31 +0200 Subject: [PATCH 27/45] Fixed server lists --- Moonlight/Shared/Views/Domains/Index.razor | 64 ++++++++++++------- Moonlight/Shared/Views/Servers/Index.razor | 15 +++-- Moonlight/Shared/Views/Websites/Index.razor | 70 ++++++++++++++++++++- Moonlight/resources/lang/de_de.lang | 4 ++ 4 files changed, 126 insertions(+), 27 deletions(-) diff --git a/Moonlight/Shared/Views/Domains/Index.razor b/Moonlight/Shared/Views/Domains/Index.razor index e98fc70d..87bdfe4d 100644 --- a/Moonlight/Shared/Views/Domains/Index.razor +++ b/Moonlight/Shared/Views/Domains/Index.razor @@ -3,49 +3,69 @@ @using Moonlight.App.Repositories.Domains @using Moonlight.App.Database.Entities @using Microsoft.EntityFrameworkCore -@using BlazorTable -@using Moonlight.App.Services -@using domain = Moonlight.App.Database.Entities.Domain @inject DomainRepository DomainRepository -@inject DomainService DomainService -@inject SmartTranslateService SmartTranslateService - @foreach (var domain in domains) + @if (Domains.Any()) { -
-
-
-
-
-
- -
- + } + } + else + { +
+
+

+ You have no domains +

+ + We were not able to find any domains associated with your account +
} @code { + [CascadingParameter] public User? User { get; set; } - - private domain[] domains { get; set; } - - public async Task Load(LazyLoader loader) + + private Domain[] Domains { get; set; } + + private Task Load(LazyLoader loader) { - domains = DomainRepository + Domains = DomainRepository .Get() .Include(x => x.SharedDomain) .Include(x => x.Owner) .Where(x => x.Owner == User) .ToArray(); + + return Task.CompletedTask; } + } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Servers/Index.razor b/Moonlight/Shared/Views/Servers/Index.razor index d5cd8c68..d2047485 100644 --- a/Moonlight/Shared/Views/Servers/Index.razor +++ b/Moonlight/Shared/Views/Servers/Index.razor @@ -73,8 +73,15 @@ } else { -
- No servers found +
+
+

+ You have no servers +

+ + We were not able to find any servers associated with your account + +
} @@ -84,8 +91,8 @@ [CascadingParameter] public User User { get; set; } - private App.Database.Entities.Server[] AllServers; - private readonly Dictionary StatusCache = new(); + private Server[] AllServers; + private readonly Dictionary StatusCache = new(); private Task Load(LazyLoader arg) { diff --git a/Moonlight/Shared/Views/Websites/Index.razor b/Moonlight/Shared/Views/Websites/Index.razor index 6bf9dd88..47d773b4 100644 --- a/Moonlight/Shared/Views/Websites/Index.razor +++ b/Moonlight/Shared/Views/Websites/Index.razor @@ -1 +1,69 @@ -@page "/websites" \ No newline at end of file +@page "/websites" +@using Moonlight.App.Database.Entities +@using Moonlight.App.Repositories +@using Microsoft.EntityFrameworkCore + +@inject WebsiteRepository WebsiteRepository + + + @if (Websites.Any()) + { + foreach (var website in Websites) + { + + } + } + else + { +
+
+

+ You have no websites +

+ + We were not able to find any websites associated with your account + +
+
+ } +
+ +@code +{ + [CascadingParameter] + public User User { get; set; } + + private Website[] Websites; + + private Task Load(LazyLoader lazyLoader) + { + Websites = WebsiteRepository + .Get() + .Include(x => x.Owner) + .Include(x => x.PleskServer) + .Where(x => x.Owner.Id == User.Id) + .ToArray(); + + return Task.CompletedTask; + } +} diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 95a82935..aa03d0d1 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -546,3 +546,7 @@ Website details;Website details Configure your website;Configure your website The name cannot be longer that 32 characters;The name cannot be longer that 32 characters The name should only consist of lower case characters;The name should only consist of lower case characters +You have no domains;You have no domains +We were not able to find any domains associated with your account;We were not able to find any domains associated with your account +You have no websites;You have no websites +We were not able to find any websites associated with your account;We were not able to find any websites associated with your account From e41a64a164f3a64eb405b99eb9c00b41f8562b12 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 12 Apr 2023 19:13:01 +0200 Subject: [PATCH 28/45] silent onlyadmin --- Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor b/Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor index e5d16a84..194c646b 100644 --- a/Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor +++ b/Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor @@ -7,7 +7,7 @@ { @ChildContent } - else + else if(!Silent) {
Missing admin permissions. This attempt has been logged ;) @@ -22,4 +22,7 @@ [CascadingParameter] public User? User { get; set; } + + [Parameter] + public bool Silent { get; set; } = false; } From 24f131f264ce0305ee413233e3b47ec67b2e055e Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 12 Apr 2023 19:33:26 +0200 Subject: [PATCH 29/45] Random fixes. Lang file --- Moonlight/App/Helpers/SmartTranslateHelper.cs | 4 ++- .../ErrorBoundaries/GlobalErrorBoundary.razor | 5 +--- .../Shared/Views/Admin/Servers/Manager.razor | 29 ++++++++++++------- Moonlight/resources/lang/de_de.lang | 3 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Moonlight/App/Helpers/SmartTranslateHelper.cs b/Moonlight/App/Helpers/SmartTranslateHelper.cs index 96b45999..309b6300 100644 --- a/Moonlight/App/Helpers/SmartTranslateHelper.cs +++ b/Moonlight/App/Helpers/SmartTranslateHelper.cs @@ -21,7 +21,9 @@ public class SmartTranslateHelper foreach (var line in lines) { var parts = line.Split(";"); - content.Add(parts[0], parts[1]); + + if(!content.ContainsKey(parts[0])) + content.Add(parts[0], parts[1]); } Languages.Add(langKey, content); diff --git a/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor index e3771cd8..489a5a6a 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor @@ -52,10 +52,7 @@ else { receivedExceptions.Add(exception); - var user = await IdentityService.Get(); - var id = user == null ? -1 : user.Id; - - Logger.Error($"[{id}] An unhanded exception occured:"); + Logger.Error($"An unhanded exception occured:"); Logger.Error(exception); await base.OnErrorAsync(exception); diff --git a/Moonlight/Shared/Views/Admin/Servers/Manager.razor b/Moonlight/Shared/Views/Admin/Servers/Manager.razor index 06aebefb..1c96755e 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Manager.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Manager.razor @@ -100,23 +100,30 @@ { await lazyLoader.SetText(node.Name); - var containerStats = await NodeService.GetContainerStats(node); - - foreach (var container in containerStats.Containers) + try { - if (Guid.TryParse(container.Name, out Guid uuid)) - { - var server = ServerRepository - .Get() - .Include(x => x.Owner) - .FirstOrDefault(x => x.Uuid == uuid); + var containerStats = await NodeService.GetContainerStats(node); - if (server != null) + foreach (var container in containerStats.Containers) + { + if (Guid.TryParse(container.Name, out Guid uuid)) { - Containers.Add(server, container); + var server = ServerRepository + .Get() + .Include(x => x.Owner) + .FirstOrDefault(x => x.Uuid == uuid); + + if (server != null) + { + Containers.Add(server, container); + } } } } + catch (Exception) + { + // ignored + } } } diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index e238982d..ab933a0d 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -554,4 +554,5 @@ You have no domains;You have no domains We were not able to find any domains associated with your account;We were not able to find any domains associated with your account You have no websites;You have no websites We were not able to find any websites associated with your account;We were not able to find any websites associated with your account -The name should only consist of lower case characters;The name should only consist of lower case characters \ No newline at end of file +Guest;Guest +You need a domain;You need a domain From 7c18a7a9a5e5495b1275ad19aba0d4dad3faa7fb Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Wed, 12 Apr 2023 20:08:55 +0200 Subject: [PATCH 30/45] news post create + edit + delete --- Moonlight/Moonlight.csproj | 1 + .../Shared/Components/News/NewsEditor.razor | 98 +++++++++++++++++++ .../Components/Partials/SidebarMenu.razor | 8 ++ Moonlight/Shared/News/Edit.razor | 32 ++++++ Moonlight/Shared/Views/News.razor | 89 +++++++++++++++++ Moonlight/resources/lang/de_de.lang | 9 +- 6 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 Moonlight/Shared/Components/News/NewsEditor.razor create mode 100644 Moonlight/Shared/News/Edit.razor create mode 100644 Moonlight/Shared/Views/News.razor diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 6d9ccd1e..b0120d81 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -24,6 +24,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Moonlight/Shared/Components/News/NewsEditor.razor b/Moonlight/Shared/Components/News/NewsEditor.razor new file mode 100644 index 00000000..a58d1b10 --- /dev/null +++ b/Moonlight/Shared/Components/News/NewsEditor.razor @@ -0,0 +1,98 @@ +@using Moonlight.App.Services +@using Moonlight.App.Database.Entities +@using BlazorMonaco + +@inject SmartTranslateService SmartTranslateService +@inject IJSRuntime JsRuntime + +
+
+

+ +

+
+ @{ + string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString(); + var date = Model.Date == default ? DateTime.Now : Model.Date; + } + @dateInt(date.Day).@dateInt(date.Month).@date.Year +
+
+
+ +
+ +
+ +@code { + // Monaco Editor + private MonacoEditor Editor; + private StandaloneEditorConstructionOptions EditorOptions; + + [Parameter] + public NewsEntry Model { get; set; } + + [Parameter] + public Func Save { get; set; } + + protected override void OnInitialized() + { + EditorOptions = new() + { + AutomaticLayout = true, + Language = "plaintext", + Value = "Loading content", + Theme = "moonlight-theme", + Contextmenu = false, + Minimap = new() + { + Enabled = false + }, + AutoIndent = true + }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JsRuntime.InvokeVoidAsync("initMonacoTheme"); + + Editor.OnDidInit = new EventCallback(this, async () => + { + EditorOptions.Language = "markdown"; + + var model = await Editor.GetModel(); + await MonacoEditorBase.SetModelLanguage(model, EditorOptions.Language); + await Editor.SetPosition(new Position() + { + Column = 0, + LineNumber = 1 + }); + + await Editor.SetValue(string.IsNullOrWhiteSpace(Model.Markdown) ? "*enter your markdown here*" : Model.Markdown); + + await Editor.Layout(new Dimension() + { + Height = 500, + Width = 1000 + }); + }); + } + } + + private async Task DoSave() + { + Model.Date = Model.Date == default ? DateTime.Now : Model.Date; + Model.Markdown = await Editor.GetValue(); + + Save?.Invoke(Model); + } + + public async Task UpdateMonacoText() + { + await Editor.SetValue(Model.Markdown); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index c7e6c692..777667c6 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -68,6 +68,14 @@ else Changelog
+ if (User.Admin) { diff --git a/Moonlight/Shared/News/Edit.razor b/Moonlight/Shared/News/Edit.razor new file mode 100644 index 00000000..aed04bf9 --- /dev/null +++ b/Moonlight/Shared/News/Edit.razor @@ -0,0 +1,32 @@ +@page "/news/edit/{Id:int}" + +@using Moonlight.App.Database.Entities +@using Moonlight.App.Repositories +@using Moonlight.Shared.Components.News + +@inject NewsEntryRepository NewsEntryRepository +@inject NavigationManager NavigationManager + + + + + + + +@code { + [Parameter] + public int Id { get; set; } + + private NewsEntry Entry; + + private async Task Load(LazyLoader loader) + { + Entry = NewsEntryRepository.Get().First(x => x.Id == Id); + } + + private async Task DoSave(NewsEntry entry) + { + NewsEntryRepository.Update(entry); + NavigationManager.NavigateTo("/news"); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/News.razor b/Moonlight/Shared/Views/News.razor new file mode 100644 index 00000000..d11f0779 --- /dev/null +++ b/Moonlight/Shared/Views/News.razor @@ -0,0 +1,89 @@ +@page "/news" +@using Moonlight.App.Repositories +@using Moonlight.App.Database.Entities +@using Markdig +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Moonlight.Shared.Components.News + +@inject NewsEntryRepository NewsEntryRepository +@inject SmartTranslateService SmartTranslateService +@inject NavigationManager NavigationManager +@inject AlertService AlertService + + + + + + + @foreach (var entry in Entries) + { +
+
+

@entry.Title

+
+ + + + + + + + + @{ + string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString(); + } + @dateInt(entry.Date.Day).@dateInt(entry.Date.Month).@entry.Date.Year +
+
+
+ @{ + var html = (MarkupString)Markdown.ToHtml(entry.Markdown); + } + + @html +
+
+ + } +
+ +@code { + private NewsEntry NewPost = new(); + private NewsEditor NewPostEditor; + + private NewsEntry[] Entries; + + private async Task Load(LazyLoader loader) + { + Entries = NewsEntryRepository.Get().OrderByDescending(x => x.Date).ToArray(); + } + + private async Task DoSaveNewPost(NewsEntry post) + { + NewsEntryRepository.Add(post); + + NavigationManager.NavigateTo(NavigationManager.Uri, true); + } + + private async Task Delete(NewsEntry entry) + { + var confirm = await AlertService.YesNo( + SmartTranslateService.Translate("Delete post"), + SmartTranslateService.Translate("Do you really want to delete the post \"") + entry.Title + "\"?", + SmartTranslateService.Translate("Yes"), + SmartTranslateService.Translate("No") + ); + + if(!confirm) return; + + NewsEntryRepository.Delete(entry); + + NavigationManager.NavigateTo(NavigationManager.Uri, true); + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 6b7ea7a2..3fe0ffb3 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -549,4 +549,11 @@ No node found to deploy to;No node found to deploy to Website details;Website details Configure your website;Configure your website The name cannot be longer that 32 characters;The name cannot be longer that 32 characters -The name should only consist of lower case characters;The name should only consist of lower case characters \ No newline at end of file +The name should only consist of lower case characters;The name should only consist of lower case characters +News;News +Title...;Title... +Enter text...;Enter text... +Saving...;Saving... +Deleting...;Deleting... +Delete post;Delete post +Do you really want to delete the post ";Do you really want to delete the post " From e0c9efd9a86c83368faa8dd0fd937ce303cec25b Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 01:38:24 +0200 Subject: [PATCH 31/45] Improved news system --- Moonlight/Moonlight.csproj | 2 + .../Navigations/AdminSystemNavigation.razor | 5 + .../Shared/Components/News/NewsEditor.razor | 98 ------------------- .../Components/Partials/SidebarMenu.razor | 8 -- Moonlight/Shared/News/Edit.razor | 32 ------ .../Shared/Views/Admin/Sys/News/Edit.razor | 78 +++++++++++++++ .../Shared/Views/Admin/Sys/News/Index.razor | 84 ++++++++++++++++ .../Shared/Views/Admin/Sys/News/New.razor | 52 ++++++++++ Moonlight/Shared/Views/Index.razor | 63 +++++++++++- Moonlight/Shared/Views/News.razor | 89 ----------------- Moonlight/resources/lang/de_de.lang | 2 + 11 files changed, 284 insertions(+), 229 deletions(-) delete mode 100644 Moonlight/Shared/Components/News/NewsEditor.razor delete mode 100644 Moonlight/Shared/News/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Sys/News/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Sys/News/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Sys/News/New.razor delete mode 100644 Moonlight/Shared/Views/News.razor diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index b0120d81..1c684c61 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -62,6 +62,8 @@ <_ContentIncludedByDefault Remove="Shared\Components\Tables\Table.razor" /> <_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Add.razor" /> <_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Edit.razor" /> + <_ContentIncludedByDefault Remove="Shared\Components\News\NewsEditor.razor" /> + <_ContentIncludedByDefault Remove="Shared\News\Edit.razor" /> diff --git a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor index 18385f6c..bb235e50 100644 --- a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor @@ -39,6 +39,11 @@ Discord bot +
diff --git a/Moonlight/Shared/Components/News/NewsEditor.razor b/Moonlight/Shared/Components/News/NewsEditor.razor deleted file mode 100644 index a58d1b10..00000000 --- a/Moonlight/Shared/Components/News/NewsEditor.razor +++ /dev/null @@ -1,98 +0,0 @@ -@using Moonlight.App.Services -@using Moonlight.App.Database.Entities -@using BlazorMonaco - -@inject SmartTranslateService SmartTranslateService -@inject IJSRuntime JsRuntime - -
-
-

- -

-
- @{ - string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString(); - var date = Model.Date == default ? DateTime.Now : Model.Date; - } - @dateInt(date.Day).@dateInt(date.Month).@date.Year -
-
-
- -
- -
- -@code { - // Monaco Editor - private MonacoEditor Editor; - private StandaloneEditorConstructionOptions EditorOptions; - - [Parameter] - public NewsEntry Model { get; set; } - - [Parameter] - public Func Save { get; set; } - - protected override void OnInitialized() - { - EditorOptions = new() - { - AutomaticLayout = true, - Language = "plaintext", - Value = "Loading content", - Theme = "moonlight-theme", - Contextmenu = false, - Minimap = new() - { - Enabled = false - }, - AutoIndent = true - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await JsRuntime.InvokeVoidAsync("initMonacoTheme"); - - Editor.OnDidInit = new EventCallback(this, async () => - { - EditorOptions.Language = "markdown"; - - var model = await Editor.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, EditorOptions.Language); - await Editor.SetPosition(new Position() - { - Column = 0, - LineNumber = 1 - }); - - await Editor.SetValue(string.IsNullOrWhiteSpace(Model.Markdown) ? "*enter your markdown here*" : Model.Markdown); - - await Editor.Layout(new Dimension() - { - Height = 500, - Width = 1000 - }); - }); - } - } - - private async Task DoSave() - { - Model.Date = Model.Date == default ? DateTime.Now : Model.Date; - Model.Markdown = await Editor.GetValue(); - - Save?.Invoke(Model); - } - - public async Task UpdateMonacoText() - { - await Editor.SetValue(Model.Markdown); - } -} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index 777667c6..c7e6c692 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -68,14 +68,6 @@ else Changelog
- if (User.Admin) { diff --git a/Moonlight/Shared/News/Edit.razor b/Moonlight/Shared/News/Edit.razor deleted file mode 100644 index aed04bf9..00000000 --- a/Moonlight/Shared/News/Edit.razor +++ /dev/null @@ -1,32 +0,0 @@ -@page "/news/edit/{Id:int}" - -@using Moonlight.App.Database.Entities -@using Moonlight.App.Repositories -@using Moonlight.Shared.Components.News - -@inject NewsEntryRepository NewsEntryRepository -@inject NavigationManager NavigationManager - - - - - - - -@code { - [Parameter] - public int Id { get; set; } - - private NewsEntry Entry; - - private async Task Load(LazyLoader loader) - { - Entry = NewsEntryRepository.Get().First(x => x.Id == Id); - } - - private async Task DoSave(NewsEntry entry) - { - NewsEntryRepository.Update(entry); - NavigationManager.NavigateTo("/news"); - } -} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/News/Edit.razor b/Moonlight/Shared/Views/Admin/Sys/News/Edit.razor new file mode 100644 index 00000000..e26a0e6d --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/News/Edit.razor @@ -0,0 +1,78 @@ +@page "/admin/system/news/edit/{Id:int}" + +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Repositories +@using Moonlight.App.Services +@using Moonlight.Shared.Components.FileManagerPartials + +@inject SmartTranslateService SmartTranslateService +@inject NavigationManager NavigationManager +@inject NewsEntryRepository NewsEntryRepository + + + + @if (Entry == null) + { +
+
+

+ No entry found +

+ + We were not able to find the news entry with this id + +
+
+ } + else + { +
+
+

+ +

+
+ @(Formatter.FormatDateOnly(Entry.Date)) +
+
+
+ +
+ +
+ } +
+
+ +@code +{ + [Parameter] + public int Id { get; set; } + + private NewsEntry? Entry; + + private FileEditor FileEditor; + + private async Task Save() + { + Entry!.Markdown = await FileEditor.GetData(); + + NewsEntryRepository.Update(Entry); + + NavigationManager.NavigateTo("/admin/system/news"); + } + + private Task Load(LazyLoader arg) + { + Entry = NewsEntryRepository.Get().FirstOrDefault(x => x.Id == Id); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/News/Index.razor b/Moonlight/Shared/Views/Admin/Sys/News/Index.razor new file mode 100644 index 00000000..337210d8 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/News/Index.razor @@ -0,0 +1,84 @@ +@page "/admin/system/news" +@using Moonlight.App.Repositories +@using Moonlight.App.Database.Entities +@using Markdig +@using Moonlight.App.Helpers +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Moonlight.Shared.Components.Navigations +@using Moonlight.Shared.Components.FileManagerPartials + +@inject NewsEntryRepository NewsEntryRepository +@inject SmartTranslateService SmartTranslateService +@inject AlertService AlertService + + + + + + + + @foreach (var entry in Entries) + { +
+
+

@entry.Title

+
+ + + + + + + + @(Formatter.FormatDateOnly(entry.Date)) +
+
+
+ @{ + var html = (MarkupString)Markdown.ToHtml(entry.Markdown); + } + + @(html) +
+
+ } +
+
+ +@code +{ + private NewsEntry[] Entries; + + private LazyLoader LazyLoader; + private FileEditor FileEditor; + + private Task Load(LazyLoader loader) + { + Entries = NewsEntryRepository.Get().OrderByDescending(x => x.Date).ToArray(); + + return Task.CompletedTask; + } + + private async Task Delete(NewsEntry entry) + { + var confirm = await AlertService.ConfirmMath(); + + if (!confirm) return; + + NewsEntryRepository.Delete(entry); + + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/News/New.razor b/Moonlight/Shared/Views/Admin/Sys/News/New.razor new file mode 100644 index 00000000..64da8468 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Sys/News/New.razor @@ -0,0 +1,52 @@ +@page "/admin/system/news/new" +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Repositories +@using Moonlight.App.Services +@using Moonlight.Shared.Components.FileManagerPartials + +@inject SmartTranslateService SmartTranslateService +@inject NavigationManager NavigationManager +@inject NewsEntryRepository NewsEntryRepository + + +
+
+

+ +

+
+ @(Formatter.FormatDateOnly(Model.Date)) +
+
+
+ +
+ +
+
+ +@code +{ + private NewsEntry Model = new() //TODO: Smart form model + { + Date = DateTime.UtcNow + }; + + private FileEditor FileEditor; + + private async Task Save() + { + Model.Markdown = await FileEditor.GetData(); + + NewsEntryRepository.Add(Model); + + NavigationManager.NavigateTo("/admin/system/news"); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Index.razor b/Moonlight/Shared/Views/Index.razor index 3a8162d4..33385548 100644 --- a/Moonlight/Shared/Views/Index.razor +++ b/Moonlight/Shared/Views/Index.razor @@ -3,14 +3,62 @@ @using Moonlight.App.Repositories.Servers @using Microsoft.EntityFrameworkCore @using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers @using Moonlight.App.Repositories @using Moonlight.App.Repositories.Domains +@using Markdig @inject ServerRepository ServerRepository @inject WebsiteRepository WebsiteRepository @inject DomainRepository DomainRepository +@inject NewsEntryRepository NewsEntryRepository +@if (NewsEntries.Any()) +{ + if (CurrentNewsIndex > NewsEntries.Count - 1) + CurrentNewsIndex = 0; + + if (CurrentNewsIndex < 0) + CurrentNewsIndex = NewsEntries.Count - 1; + + var currentEntry = NewsEntries[CurrentNewsIndex]; + +
+
+
+
+ + + +

@(Formatter.FormatDateOnly(currentEntry.Date))

+
+
+ +
+
+
+ @{ + var html = (MarkupString)Markdown.ToHtml(currentEntry.Markdown); + } + + @(html) +
+
+
+} +
@@ -193,11 +241,14 @@ { [CascadingParameter] public User User { get; set; } - + private int ServerCount = 0; private int DomainCount = 0; private int WebsiteCount = 0; + private List NewsEntries; + private int CurrentNewsIndex = 0; + private Task Load(LazyLoader lazyLoader) { ServerCount = ServerRepository @@ -214,7 +265,15 @@ .Get() .Include(x => x.Owner) .Count(x => x.Owner.Id == User.Id); - + + NewsEntries = NewsEntryRepository.Get().ToList(); + return Task.CompletedTask; } + + private async Task ChangeNewsIndex(int i) + { + CurrentNewsIndex += i; + await InvokeAsync(StateHasChanged); + } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/News.razor b/Moonlight/Shared/Views/News.razor deleted file mode 100644 index d11f0779..00000000 --- a/Moonlight/Shared/Views/News.razor +++ /dev/null @@ -1,89 +0,0 @@ -@page "/news" -@using Moonlight.App.Repositories -@using Moonlight.App.Database.Entities -@using Markdig -@using Moonlight.App.Services -@using Moonlight.App.Services.Interop -@using Moonlight.Shared.Components.News - -@inject NewsEntryRepository NewsEntryRepository -@inject SmartTranslateService SmartTranslateService -@inject NavigationManager NavigationManager -@inject AlertService AlertService - - - - - - - @foreach (var entry in Entries) - { -
-
-

@entry.Title

-
- - - - - - - - - @{ - string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString(); - } - @dateInt(entry.Date.Day).@dateInt(entry.Date.Month).@entry.Date.Year -
-
-
- @{ - var html = (MarkupString)Markdown.ToHtml(entry.Markdown); - } - - @html -
-
- - } - - -@code { - private NewsEntry NewPost = new(); - private NewsEditor NewPostEditor; - - private NewsEntry[] Entries; - - private async Task Load(LazyLoader loader) - { - Entries = NewsEntryRepository.Get().OrderByDescending(x => x.Date).ToArray(); - } - - private async Task DoSaveNewPost(NewsEntry post) - { - NewsEntryRepository.Add(post); - - NavigationManager.NavigateTo(NavigationManager.Uri, true); - } - - private async Task Delete(NewsEntry entry) - { - var confirm = await AlertService.YesNo( - SmartTranslateService.Translate("Delete post"), - SmartTranslateService.Translate("Do you really want to delete the post \"") + entry.Title + "\"?", - SmartTranslateService.Translate("Yes"), - SmartTranslateService.Translate("No") - ); - - if(!confirm) return; - - NewsEntryRepository.Delete(entry); - - NavigationManager.NavigateTo(NavigationManager.Uri, true); - } -} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 6bb869e6..eb4bb032 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -563,3 +563,5 @@ You have no websites;You have no websites We were not able to find any websites associated with your account;We were not able to find any websites associated with your account Guest;Guest You need a domain;You need a domain +New post;New post +New entry;New entry From 8b88f1c48250441a332eceb8d90d757de5e116ca Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:52:30 +0200 Subject: [PATCH 32/45] domain view redesigned | fixed some masu at midnight --- .../WebsiteControl/WebsiteDatabases.razor | 139 +++++++++--------- Moonlight/Shared/Views/Websites/Create.razor | 5 +- Moonlight/resources/lang/de_de.lang | 1 + 3 files changed, 75 insertions(+), 70 deletions(-) diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor index 3679564f..2c244069 100644 --- a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor +++ b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor @@ -6,96 +6,97 @@ @inject SmartTranslateService SmartTranslateService @inject WebsiteService WebsiteService -
- -
- -
- -
- - - -
-
-
-
+ +
+
+ +
+ +
+ + + +
+
+
-
- @if (Databases.Any()) - { -
- @foreach (var database in Databases) - { -
-

- -

-
-
-
-
+
+ @if (Databases.Any()) + { + @foreach (var databases in Databases.Chunk(2)) + { +
+ @foreach (var database in databases) + { +
+
+
+
+ @(database.Name) - @(database.Type.ToUpper().Replace("MYSQL", "MySQL")) +
+
+
+ + + + + + + + + + + + + + + + + +
- -
+
- - -
-
+
- -
+
- - -
-
+
- -
+
- - -
-
+
- -
+
- - -
- -
+
+
+
- } -
- } - else - { -
- No databases found for this website -
- } +
+ } +
+ } + } + else + { +
+ No databases found for this website
- - -
+ } + @code { diff --git a/Moonlight/Shared/Views/Websites/Create.razor b/Moonlight/Shared/Views/Websites/Create.razor index 5533cd19..79b9d75d 100644 --- a/Moonlight/Shared/Views/Websites/Create.razor +++ b/Moonlight/Shared/Views/Websites/Create.razor @@ -132,7 +132,10 @@ private async Task OnValidSubmit() { - if (WebsiteRepository.Get().Count() < (await SubscriptionService.GetLimit("websites")).Amount) + if (WebsiteRepository + .Get() + .Include(x => x.Owner) + .Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount) { var website = await WebsiteService.Create(Model.BaseDomain, User, PleskServer); diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index eb4bb032..dc4f01e0 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -565,3 +565,4 @@ Guest;Guest You need a domain;You need a domain New post;New post New entry;New entry +You reached the maximum amount of websites in your subscription;You reached the maximum amount of websites in your subscription From 601ba7d3723713431a59f37c9feb1856166446cb Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 16:29:40 +0200 Subject: [PATCH 33/45] Implemented new storage system, path builder, default resources, fixed lang stuff --- .gitignore | 4 +- Moonlight/App/Helpers/PathBuilder.cs | 28 + Moonlight/App/Helpers/SmartTranslateHelper.cs | 4 +- .../Api/Moonlight/ResourcesController.cs | 8 +- Moonlight/App/Services/ConfigService.cs | 34 +- Moonlight/App/Services/StorageService.cs | 58 ++ Moonlight/Dockerfile | 2 + Moonlight/Moonlight.csproj | 2 +- Moonlight/Program.cs | 1 + Moonlight/README.md | 13 + .../Shared/Views/Admin/Sys/Resources.razor | 5 +- Moonlight/defaultstorage/configs/config.json | 78 +++ .../resources/lang/de_de.lang | 0 .../resources/lang/en_us.lang | 0 .../resources/mail/login.html | 0 .../resources/mail/passwordChange.html | 0 .../resources/mail/passwordReset.html | 0 .../resources/mail/register.html | 0 .../resources/public/images/logo.svg | 0 .../resources/public/images/logolong.png | Bin Moonlight/deleteme_resources/lang/de_de.lang | 567 ++++++++++++++++++ Moonlight/deleteme_resources/lang/en_us.lang | 86 +++ Moonlight/deleteme_resources/mail/login.html | 53 ++ .../mail/passwordChange.html | 53 ++ .../mail/passwordReset.html | 54 ++ .../deleteme_resources/mail/register.html | 50 ++ .../deleteme_resources/public/images/logo.svg | 14 + .../public/images/logolong.png | Bin 0 -> 12410 bytes 28 files changed, 1096 insertions(+), 18 deletions(-) create mode 100644 Moonlight/App/Helpers/PathBuilder.cs create mode 100644 Moonlight/App/Services/StorageService.cs create mode 100644 Moonlight/README.md create mode 100644 Moonlight/defaultstorage/configs/config.json rename Moonlight/{ => defaultstorage}/resources/lang/de_de.lang (100%) rename Moonlight/{ => defaultstorage}/resources/lang/en_us.lang (100%) rename Moonlight/{ => defaultstorage}/resources/mail/login.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/passwordChange.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/passwordReset.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/register.html (100%) rename Moonlight/{ => defaultstorage}/resources/public/images/logo.svg (100%) rename Moonlight/{ => defaultstorage}/resources/public/images/logolong.png (100%) create mode 100644 Moonlight/deleteme_resources/lang/de_de.lang create mode 100644 Moonlight/deleteme_resources/lang/en_us.lang create mode 100644 Moonlight/deleteme_resources/mail/login.html create mode 100644 Moonlight/deleteme_resources/mail/passwordChange.html create mode 100644 Moonlight/deleteme_resources/mail/passwordReset.html create mode 100644 Moonlight/deleteme_resources/mail/register.html create mode 100644 Moonlight/deleteme_resources/public/images/logo.svg create mode 100644 Moonlight/deleteme_resources/public/images/logolong.png diff --git a/.gitignore b/.gitignore index 4ec99a85..7487adb3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ _UpgradeReport_Files/ Thumbs.db Desktop.ini .DS_Store -.idea/.idea.Moonlight/.idea/discord.xml \ No newline at end of file +.idea/.idea.Moonlight/.idea/discord.xml + +storage/ \ No newline at end of file diff --git a/Moonlight/App/Helpers/PathBuilder.cs b/Moonlight/App/Helpers/PathBuilder.cs new file mode 100644 index 00000000..0b3e98bd --- /dev/null +++ b/Moonlight/App/Helpers/PathBuilder.cs @@ -0,0 +1,28 @@ +namespace Moonlight.App.Helpers; + +public class PathBuilder +{ + public static string Dir(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + Path.DirectorySeparatorChar; + } + + return res; + } + + public static string File(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar); + } + + return res; + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/SmartTranslateHelper.cs b/Moonlight/App/Helpers/SmartTranslateHelper.cs index 309b6300..6ce8d656 100644 --- a/Moonlight/App/Helpers/SmartTranslateHelper.cs +++ b/Moonlight/App/Helpers/SmartTranslateHelper.cs @@ -8,7 +8,7 @@ public class SmartTranslateHelper { Languages = new(); - foreach (var file in Directory.GetFiles("resources/lang")) + foreach (var file in Directory.GetFiles(PathBuilder.Dir("storage", "resources", "lang"))) { if (Path.GetExtension(file) == ".lang") { @@ -40,7 +40,7 @@ public class SmartTranslateHelper { Languages[langKey].Add(content, content); - File.WriteAllLines($"resources/lang/{langKey}.lang", GenerateData(Languages[langKey])); + File.WriteAllLines(PathBuilder.File("storage", "resources", "lang", $"{langKey}.lang"), GenerateData(Languages[langKey])); } return Languages[langKey][content]; diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs index 52750c40..254a0b5a 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs @@ -1,5 +1,6 @@ using Logging.Net; using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Helpers; using Moonlight.App.Models.Misc; using Moonlight.App.Services.LogServices; @@ -28,14 +29,13 @@ public class ResourcesController : Controller return NotFound(); } - if (System.IO.File.Exists($"resources/public/images/{name}")) + if (System.IO.File.Exists(PathBuilder.File("storage", "resources", "public", "images", name))) { - var fs = new FileStream($"resources/public/images/{name}", FileMode.Open); + var fs = new FileStream(PathBuilder.File("storage", "resources", "public", "images", name), FileMode.Open); return File(fs, MimeTypes.GetMimeType(name), name); } - - Logger.Debug("404 on resources"); + return NotFound(); } } \ No newline at end of file diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index ed124f62..27bc5bc7 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -7,26 +7,44 @@ namespace Moonlight.App.Services; public class ConfigService : IConfiguration { + private readonly StorageService StorageService; + private IConfiguration Configuration; public bool DebugMode { get; private set; } = false; - - public ConfigService() + + public ConfigService(StorageService storageService) { - Configuration = new ConfigurationBuilder().AddJsonStream( - new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText("..\\..\\appsettings.json"))) - ).Build(); + StorageService = storageService; + StorageService.EnsureCreated(); + + Reload(); // Env vars var debugVar = Environment.GetEnvironmentVariable("ML_DEBUG"); if (debugVar != null) DebugMode = bool.Parse(debugVar); - - if(DebugMode) + + if (DebugMode) Logger.Debug("Debug mode enabled"); } - + + public void Reload() + { + Logger.Info($"Reading config from '{PathBuilder.File("storage", "configs", "config.json")}'"); + + Configuration = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + File.ReadAllText( + PathBuilder.File("storage", "configs", "config.json") + ) + ) + )).Build(); + + Logger.Info("Reloaded configuration file"); + } + public IEnumerable GetChildren() { return Configuration.GetChildren(); diff --git a/Moonlight/App/Services/StorageService.cs b/Moonlight/App/Services/StorageService.cs new file mode 100644 index 00000000..455e5329 --- /dev/null +++ b/Moonlight/App/Services/StorageService.cs @@ -0,0 +1,58 @@ +using Logging.Net; +using Moonlight.App.Helpers; + +namespace Moonlight.App.Services; + +public class StorageService +{ + public StorageService() + { + EnsureCreated(); + } + + public void EnsureCreated() + { + Directory.CreateDirectory(PathBuilder.Dir("storage", "uploads")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "configs")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "resources")); + + if(IsEmpty(PathBuilder.Dir("storage", "resources"))) + { + Logger.Info("Default resources not found. Copying default resources"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "resources"), + PathBuilder.Dir("storage", "resources") + ); + } + + if (IsEmpty(PathBuilder.Dir("storage", "configs"))) + { + Logger.Info("Default configs not found. Copying default configs"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "configs"), + PathBuilder.Dir("storage", "configs") + ); + } + } + + private bool IsEmpty(string path) + { + return !Directory.EnumerateFiles(path).Any(); + } + private static void CopyFilesRecursively(string sourcePath, string targetPath) + { + //Now Create all of the directories + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath)); + } + + //Copy all the files & Replaces any files with the same name + foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true); + } + } +} \ No newline at end of file diff --git a/Moonlight/Dockerfile b/Moonlight/Dockerfile index 8f612a2c..4fecfff7 100644 --- a/Moonlight/Dockerfile +++ b/Moonlight/Dockerfile @@ -19,4 +19,6 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +RUN mkdir /app/storage +RUN rm -r /app/storage/* ENTRYPOINT ["dotnet", "Moonlight.dll"] \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 1c684c61..e2a9aa4a 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -70,7 +70,7 @@ - + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 8651fe58..76e01893 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -74,6 +74,7 @@ namespace Moonlight // Services builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/README.md b/Moonlight/README.md new file mode 100644 index 00000000..264af4c5 --- /dev/null +++ b/Moonlight/README.md @@ -0,0 +1,13 @@ +### Some explanations + +defaultstorage: + +This directory is for the default assets of a moonlight instance, e.g. lang files, images etc + +storage: + +This directory is empty in fresh moonlight instances and will be populated with example config upon first run. +Before using moonlight this config file has to be modified. +Also resources are going to be copied from the default storage to this storage. +To access files in this storage we recommend to use the PathBuilder functions to ensure cross platform compatibility. +The storage directory should be mounted to a specific path when using docker container so when the container is replaced/rebuild your storage will not be modified \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/Resources.razor b/Moonlight/Shared/Views/Admin/Sys/Resources.razor index 28a53119..982b4064 100644 --- a/Moonlight/Shared/Views/Admin/Sys/Resources.razor +++ b/Moonlight/Shared/Views/Admin/Sys/Resources.razor @@ -3,12 +3,13 @@ @using Moonlight.Shared.Components.FileManagerPartials @using Moonlight.App.Helpers.Files @using Moonlight.Shared.Components.Navigations +@using Moonlight.App.Helpers
- - + +
\ No newline at end of file diff --git a/Moonlight/defaultstorage/configs/config.json b/Moonlight/defaultstorage/configs/config.json new file mode 100644 index 00000000..6e9ff6dd --- /dev/null +++ b/Moonlight/defaultstorage/configs/config.json @@ -0,0 +1,78 @@ +{ + "Moonlight": { + "AppUrl": "http://your-moonlight-url.test", + "Database": { + "Database": "database_name", + "Host": "your-moonlight-database-host.de", + "Password": "s3cr3t", + "Port": "10324", + "Username": "user_name" + }, + "DiscordBot": { + "Enable": "True", + "Token": "Discord.Token.Here" + }, + "Domains": { + "_comment": "Cloudflare Api Credentials", + "AccountId": "Account Id here", + "Email": "Cloudflare Email here", + "Key": "Api Key Here" + }, + "Html": { + "Headers": { + "Color": "#4b27e8", + "Description": "the next generation hosting panel", + "Keywords": "moonlight", + "Title": "Moonlight - moonlight.tld" + } + }, + "Marketing": { + "BrandName": "My cool project", + "Imprint": "https://mycoolproject.de/imprint", + "Privacy": "https://mycoolproject.de/privacy", + "Website": "https://mycoolproject.de/" + }, + "OAuth2": { + "Discord": { + "ClientId": "10324", + "ClientSecret": "s3cr3t", + "Enable": "True" + }, + "Google": { + "ClientId": "xyz.apps.googleusercontent.com", + "ClientSecret": "s3cr3t", + "Enable": "True" + }, + "EnableOverrideUrl": "True", + "OverrideUrl": "http://your-moonlight-url.test" + }, + "Security": { + "Token": "RANDOM UUID HERE" + }, + "SetupComplete": "True", + "SupportChat": { + "Enabled": "True" + }, + "Mail": { + "Email": "no-reply@mycoolproject.de", + "Server": "mycoolproject.de", + "Password": "s3cr3t", + "Port": 25 + }, + "Cleanup": { + "Cpu": 90, + "Memory": 8192, + "Wait": 15, + "Uptime": 6, + "Enable": false, + "MinUptime": 10 + }, + "Subscriptions": { + "_comment": "Not implemented", + "SellPass": { + "Enable": false, + "Url": "" + } + } + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/defaultstorage/resources/lang/de_de.lang similarity index 100% rename from Moonlight/resources/lang/de_de.lang rename to Moonlight/defaultstorage/resources/lang/de_de.lang diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/defaultstorage/resources/lang/en_us.lang similarity index 100% rename from Moonlight/resources/lang/en_us.lang rename to Moonlight/defaultstorage/resources/lang/en_us.lang diff --git a/Moonlight/resources/mail/login.html b/Moonlight/defaultstorage/resources/mail/login.html similarity index 100% rename from Moonlight/resources/mail/login.html rename to Moonlight/defaultstorage/resources/mail/login.html diff --git a/Moonlight/resources/mail/passwordChange.html b/Moonlight/defaultstorage/resources/mail/passwordChange.html similarity index 100% rename from Moonlight/resources/mail/passwordChange.html rename to Moonlight/defaultstorage/resources/mail/passwordChange.html diff --git a/Moonlight/resources/mail/passwordReset.html b/Moonlight/defaultstorage/resources/mail/passwordReset.html similarity index 100% rename from Moonlight/resources/mail/passwordReset.html rename to Moonlight/defaultstorage/resources/mail/passwordReset.html diff --git a/Moonlight/resources/mail/register.html b/Moonlight/defaultstorage/resources/mail/register.html similarity index 100% rename from Moonlight/resources/mail/register.html rename to Moonlight/defaultstorage/resources/mail/register.html diff --git a/Moonlight/resources/public/images/logo.svg b/Moonlight/defaultstorage/resources/public/images/logo.svg similarity index 100% rename from Moonlight/resources/public/images/logo.svg rename to Moonlight/defaultstorage/resources/public/images/logo.svg diff --git a/Moonlight/resources/public/images/logolong.png b/Moonlight/defaultstorage/resources/public/images/logolong.png similarity index 100% rename from Moonlight/resources/public/images/logolong.png rename to Moonlight/defaultstorage/resources/public/images/logolong.png diff --git a/Moonlight/deleteme_resources/lang/de_de.lang b/Moonlight/deleteme_resources/lang/de_de.lang new file mode 100644 index 00000000..eb4bb032 --- /dev/null +++ b/Moonlight/deleteme_resources/lang/de_de.lang @@ -0,0 +1,567 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Login;Login +Register;Register +Insert brand name...;Insert brand name... +Save and continue;Save and continue +Saving;Saving +Configure basics;Configure basics +Brand name;Brand name +test;test +Insert first name...;Insert first name... +Insert last name...;Insert last name... +Insert email address...;Insert email address... +Add;Add +Adding...;Adding... +Add admin accounts;Add admin accounts +First name;First name +Last name;Last name +Email address;Email address +Enter password;Enter password +Next;Next +Back;Back +Configure features;Configure features +Support chat;Support chat +Finish;Finish +Finalize installation;Finalize installation +Moonlight basic settings successfully configured;Moonlight basic settings successfully configured +Ooops. This page is crashed;Ooops. This page is crashed +This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page;This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page +Setup complete;Setup complete +It looks like this moonlight instance is ready to go;It looks like this moonlight instance is ready to go +User successfully created;User successfully created +Ooops. Your moonlight client is crashed;Ooops. Your moonlight client is crashed +This error has been reported to the moonlight team;This error has been reported to the moonlight team +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up +Authenticating;Authenticating +Sign in with Google;Sign in with Google +Working;Working +Error;Error +Email and password combination not found;Email and password combination not found +Email;Email +Password;Password +Account settings;Account settings +Logout;Logout +Dashboard;Dashboard +Order;Order +Website;Website +Database;Database +Domain;Domain +Servers;Servers +Websites;Websites +Databases;Databases +Domains;Domains +Changelog;Changelog +Firstname;Firstname +Lastname;Lastname +Repeat password;Repeat password +Sign Up;Sign Up +Sign up to start with moonlight;Sign up to start with moonlight +Sign up with Discord;Sign up with Discord +Sign up with Google;Sign up with Google +Sign-up;Sign-up +Already registered?;Already registered? +Sign in;Sign in +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a database;Create a database +A quick way to store your data and manage it from all around the world;A quick way to store your data and manage it from all around the world +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your databases;Manage your databases +Insert, delete and update the data in your databases;Insert, delete and update the data in your databases +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records +Admin;Admin +System;System +Overview;Overview +Manager;Manager +Cleanup;Cleanup +Nodes;Nodes +Images;Images +aaPanel;aaPanel +Users;Users +Support;Support +Statistics;Statistics +No nodes found. Start with adding a new node;No nodes found. Start with adding a new node +Nodename;Nodename +FQDN;FQDN +Create;Create +Creating;Creating +Http port;Http port +Sftp port;Sftp port +Moonlight daemon port;Moonlight daemon port +SSL;SSL +CPU Usage;CPU Usage +In %;In % +Memory;Memory +Used / Available memory;Used / Available memory +Storage;Storage +Available storage;Available storage +Add a new node;Add a new node +Delete;Delete +Deleting;Deleting +Edit;Edit +Token Id;Token Id +Token;Token +Save;Save +Setup;Setup +Open a ssh connection to your node and enter;Open a ssh connection to your node and enter +and paste the config below. Then press STRG+O and STRG+X to save;and paste the config below. Then press STRG+O and STRG+X to save +Before configuring this node, install the daemon;Before configuring this node, install the daemon +Delete this node?;Delete this node? +Do you really want to delete this node;Do you really want to delete this node +Yes;Yes +No;No +Status;Status +Adding;Adding +Port;Port +Id;Id +Manage;Manage +Create new server;Create new server +No servers found;No servers found +Server name;Server name +Cpu cores;Cpu cores +Disk;Disk +Image;Image +Override startup;Override startup +Docker image;Docker image +CPU Cores (100% = 1 Core);CPU Cores (100% = 1 Core) +Server successfully created;Server successfully created +Name;Name +Cores;Cores +Owner;Owner +Value;Value +An unknown error occured;An unknown error occured +No allocation found;No allocation found +Identifier;Identifier +UuidIdentifier;UuidIdentifier +Override startup command;Override startup command +Loading;Loading +Offline;Offline +Connecting;Connecting +Start;Start +Restart;Restart +Stop;Stop +Shared IP;Shared IP +Server ID;Server ID +Cpu;Cpu +Console;Console +Files;Files +Backups;Backups +Network;Network +Plugins;Plugins +Settings;Settings +Enter command;Enter command +Execute;Execute +Checking disk space;Checking disk space +Updating config files;Updating config files +Checking file permissions;Checking file permissions +Downloading server image;Downloading server image +Downloaded server image;Downloaded server image +Starting;Starting +Online;Online +Kill;Kill +Stopping;Stopping +Search files and folders;Search files and folders +Launch WinSCP;Launch WinSCP +New folder;New folder +Upload;Upload +File name;File name +File size;File size +Last modified;Last modified +Cancel;Cancel +Canceling;Canceling +Running;Running +Loading backups;Loading backups +Started backup creation;Started backup creation +Backup is going to be created;Backup is going to be created +Rename;Rename +Move;Move +Archive;Archive +Unarchive;Unarchive +Download;Download +Starting download;Starting download +Backup successfully created;Backup successfully created +Restore;Restore +Copy url;Copy url +Backup deletion started;Backup deletion started +Backup successfully deleted;Backup successfully deleted +Primary;Primary +This feature is currently not available;This feature is currently not available +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you + minutes ago; minutes ago +just now;just now +less than a minute ago;less than a minute ago +1 hour ago;1 hour ago +1 minute ago;1 minute ago +Failed;Failed + hours ago; hours ago +Open tickets;Open tickets +Actions;Actions +No support ticket is currently open;No support ticket is currently open +User information;User information +Close ticket;Close ticket +Closing;Closing +The support team has been notified. Please be patient;The support team has been notified. Please be patient +The ticket is now closed. Type a message to open it again;The ticket is now closed. Type a message to open it again +1 day ago;1 day ago +is typing;is typing +are typing;are typing +No domains available;No domains available +Shared domains;Shared domains +Shared domain;Shared domain +Shared domain successfully deleted;Shared domain successfully deleted +Shared domain successfully added;Shared domain successfully added +Domain name;Domain name +DNS records for;DNS records for +Fetching dns records;Fetching dns records +No dns records found;No dns records found +Content;Content +Priority;Priority +Ttl;Ttl +Enable cloudflare proxy;Enable cloudflare proxy +CF Proxy;CF Proxy + days ago; days ago +Cancle;Cancle +An unexpected error occured;An unexpected error occured +Testy;Testy +Error from cloudflare api;Error from cloudflare api +Profile;Profile +No subscription available;No subscription available +Buy;Buy +Redirecting;Redirecting +Apply;Apply +Applying code;Applying code +Invalid subscription code;Invalid subscription code +Cancel Subscription;Cancel Subscription +Active until;Active until +We will send you a notification upon subscription expiration;We will send you a notification upon subscription expiration +This token has been already used;This token has been already used +New login for;New login for +No records found for this day;No records found for this day +Change;Change +Changing;Changing +Minecraft version;Minecraft version +Build version;Build version +Server installation is currently running;Server installation is currently running +Selected;Selected +Move deleted;Move deleted +Delete selected;Delete selected +Log level;Log level +Log message;Log message +Time;Time +Version;Version +You are running moonlight version;You are running moonlight version +Operating system;Operating system +Moonlight is running on;Moonlight is running on +Memory usage;Memory usage +Moonlight is using;Moonlight is using +of memory;of memory +Cpu usage;Cpu usage +Refresh;Refresh +Send a message to all users;Send a message to all users +IP;IP +URL;URL +Device;Device +Change url;Change url +Message;Message +Enter message;Enter message +Enter the message to send;Enter the message to send +Confirm;Confirm +Are you sure?;Are you sure? +Enter url;Enter url +An unknown error occured while starting backup deletion;An unknown error occured while starting backup deletion +Success;Success +Backup URL successfully copied to your clipboard;Backup URL successfully copied to your clipboard +Backup restore started;Backup restore started +Backup successfully restored;Backup successfully restored +Register for;Register for +Core;Core +Logs;Logs +AuditLog;AuditLog +SecurityLog;SecurityLog +ErrorLog;ErrorLog +Resources;Resources +WinSCP cannot be launched here;WinSCP cannot be launched here +Create a new folder;Create a new folder +Enter a name;Enter a name +File upload complete;File upload complete +New server;New server +Sessions;Sessions +New user;New user +Created at;Created at +Mail template not found;Mail template not found +Missing admin permissions. This attempt has been logged ;) +Address;Address +City;City +State;State +Country;Country +Totp;Totp +Discord;Discord +Subscription;Subscription +None;None +No user with this id found;No user with this id found +Back to list;Back to list +New domain;New domain +Reset password;Reset password +Password reset;Password reset +Reset the password of your account;Reset the password of your account +Wrong here?;Wrong here? +A user with this email can not be found;A user with this email can not be found +Passwort reset successfull. Check your mail;Passwort reset successfull. Check your mail +Discord bot;Discord bot +New image;New image +Description;Description +Uuid;Uuid +Enter tag name;Enter tag name +Remove;Remove +No tags found;No tags found +Enter docker image name;Enter docker image name +Tags;Tags +Docker images;Docker images +Default image;Default image +Startup command;Startup command +Install container;Install container +Install entry;Install entry +Configuration files;Configuration files +Startup detection;Startup detection +Stop command;Stop command +Successfully saved image;Successfully saved image +No docker images found;No docker images found +Key;Key +Default value;Default value +Allocations;Allocations +No variables found;No variables found +Successfully added image;Successfully added image +Password change for;Password change for +of;of +New node;New node +Fqdn;Fqdn +Cores used;Cores used +used;used +5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64 +Host system information;Host system information +0;0 +Docker containers running;Docker containers running +details;details +1;1 +2;2 +DDos;DDos +No ddos attacks found;No ddos attacks found +Node;Node +Date;Date +DDos attack started;DDos attack started +packets;packets +DDos attack stopped;DDos attack stopped + packets; packets +Stop all;Stop all +Kill all;Kill all +Network in;Network in +Network out;Network out +Kill all servers;Kill all servers +Do you really want to kill all running servers?;Do you really want to kill all running servers? +Change power state for;Change power state for +to;to +Stop all servers;Stop all servers +Do you really want to stop all running servers?;Do you really want to stop all running servers? +Manage ;Manage +Manage user ;Manage user +Reloading;Reloading +Update;Update +Updating;Updating +Successfully updated user;Successfully updated user +Discord id;Discord id +Discord username;Discord username +Discord discriminator;Discord discriminator +The Name field is required.;The Name field is required. +An error occured while logging you in;An error occured while logging you in +You need to enter an email address;You need to enter an email address +You need to enter a password;You need to enter a password +You need to enter a password with minimum 8 characters in lenght;You need to enter a password with minimum 8 characters in lenght +Proccessing;Proccessing +The FirstName field is required.;The FirstName field is required. +The LastName field is required.;The LastName field is required. +The Address field is required.;The Address field is required. +The City field is required.;The City field is required. +The State field is required.;The State field is required. +The Country field is required.;The Country field is required. +Street and house number requered;Street and house number requered +Max lenght reached;Max lenght reached +Server;Server +stopped;stopped +Cleanups;Cleanups +executed;executed +Used clanup;Used clanup +Enable;Enable +Disabble;Disabble +Disable;Disable +Addons;Addons +Javascript version;Javascript version +Javascript file;Javascript file +Select javascript file to execute on start;Select javascript file to execute on start +Submit;Submit +Processing;Processing +Go up;Go up +Running cleanup;Running cleanup +servers;servers +Select folder to move the file(s) to;Select folder to move the file(s) to +Paper version;Paper version +Join2Start;Join2Start +Server reset;Server reset +Reset;Reset +Resetting;Resetting +Are you sure you want to reset this server?;Are you sure you want to reset this server? +Are you sure? This cannot be undone;Are you sure? This cannot be undone +Resetting server;Resetting server +Deleted file;Deleted file +Reinstalling server;Reinstalling server +Uploading files;Uploading files +complete;complete +Upload complete;Upload complete +Security;Security +Subscriptions;Subscriptions +2fa Code;2fa Code +Your account is secured with 2fa;Your account is secured with 2fa +anyone write a fancy text here?;anyone write a fancy text here? +Activate 2fa;Activate 2fa +2fa apps;2fa apps +Use an app like ;Use an app like +or;or +and scan the following QR Code;and scan the following QR Code +If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;If you have trouble using the QR Code, select manual input in the app and enter your email and the following code: +Finish activation;Finish activation +2fa Code requiered;2fa Code requiered +New password;New password +Secure your account;Secure your account +2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login. +New subscription;New subscription +You need to enter a name;You need to enter a name +You need to enter a description;You need to enter a description +Add new limit;Add new limit +Create subscription;Create subscription +Options;Options +Amount;Amount +Do you really want to delete it?;Do you really want to delete it? +Loading your subscription;Loading your subscription +Searching for deploy node;Searching for deploy node +Searching for available images;Searching for available images +Server details;Server details +Configure your server;Configure your server +Default;Default +You reached the maximum amount of servers for every image of your subscription;You reached the maximum amount of servers for every image of your subscription +Personal information;Personal information +Enter code;Enter code +Server rename;Server rename +Create code;Create code +Save subscription;Save subscription +Enter your information;Enter your information +You need to enter your full name in order to use moonlight;You need to enter your full name in order to use moonlight +No node found;No node found +No node found to deploy to found;No node found to deploy to found +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Server not found;Server not found +A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server +Compress;Compress +Decompress;Decompress +Moving;Moving +Compressing;Compressing +selected;selected +New website;New website +Plesk servers;Plesk servers +Base domain;Base domain +Plesk server;Plesk server +Ftp;Ftp +No SSL certificate found;No SSL certificate found +Ftp Host;Ftp Host +Ftp Port;Ftp Port +Ftp Username;Ftp Username +Ftp Password;Ftp Password +Use;Use +SSL Certificates;SSL Certificates +SSL certificates;SSL certificates +Issue certificate;Issue certificate +New plesk server;New plesk server +Api url;Api url +Host system offline;Host system offline +The host system the website is running on is currently offline;The host system the website is running on is currently offline +No SSL certificates found;No SSL certificates found +No databases found for this website;No databases found for this website +The name should be at least 8 characters long;The name should be at least 8 characters long +The name should only contain of lower case characters and numbers;The name should only contain of lower case characters and numbers +Error from plesk;Error from plesk +Host;Host +Username;Username +SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one +The User field is required.;The User field is required. +You need to specify a owner;You need to specify a owner +You need to specify a image;You need to specify a image +Api Url;Api Url +Api Key;Api Key +Duration;Duration +Enter duration of subscription;Enter duration of subscription +Copied code to clipboard;Copied code to clipboard +Invalid or expired subscription code;Invalid or expired subscription code +Current subscription;Current subscription +You need to specify a server image;You need to specify a server image +CPU;CPU +Hour;Hour +Day;Day +Month;Month +Year;Year +All time;All time +This function is not implemented;This function is not implemented +Domain details;Domain details +Configure your domain;Configure your domain +You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription +You need to specify a shared domain;You need to specify a shared domain +A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain +The Email field is required.;The Email field is required. +The Password field is required.;The Password field is required. +The ConfirmPassword field is required.;The ConfirmPassword field is required. +Passwords need to match;Passwords need to match +Cleanup exception;Cleanup exception +No shared domain found;No shared domain found +Searching for deploy plesk server;Searching for deploy plesk server +No plesk server found;No plesk server found +No plesk server found to deploy to;No plesk server found to deploy to +No node found to deploy to;No node found to deploy to +Website details;Website details +Configure your website;Configure your website +The name cannot be longer that 32 characters;The name cannot be longer that 32 characters +The name should only consist of lower case characters;The name should only consist of lower case characters +News;News +Title...;Title... +Enter text...;Enter text... +Saving...;Saving... +Deleting...;Deleting... +Delete post;Delete post +Do you really want to delete the post ";Do you really want to delete the post " +You have no domains;You have no domains +We were not able to find any domains associated with your account;We were not able to find any domains associated with your account +You have no websites;You have no websites +We were not able to find any websites associated with your account;We were not able to find any websites associated with your account +Guest;Guest +You need a domain;You need a domain +New post;New post +New entry;New entry diff --git a/Moonlight/deleteme_resources/lang/en_us.lang b/Moonlight/deleteme_resources/lang/en_us.lang new file mode 100644 index 00000000..2e2e1fcb --- /dev/null +++ b/Moonlight/deleteme_resources/lang/en_us.lang @@ -0,0 +1,86 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Create;Create +Server;Server +Domain;Domain +Website;Website +Login;Login +Register;Register +Email;Email +Password;Password +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Sign in with Google;Sign in with Google +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up +Profile;Profile +Logout;Logout +Dashboard;Dashboard +Servers;Servers +Websites;Websites +Domains;Domains +Changelog;Changelog +Admin;Admin +System;System +Overview;Overview +Manager;Manager +Cleanup;Cleanup +Nodes;Nodes +Images;Images +Users;Users +Shared domains;Shared domains +Support;Support +Subscriptions;Subscriptions +Statistics;Statistics +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records +New server;New server +Id;Id +Name;Name +Cores;Cores +Memory;Memory +Disk;Disk +Owner;Owner +Manage;Manage +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Sessions;Sessions +New user;New user +First name;First name +Last name;Last name +Created at;Created at +Refresh;Refresh +Send a message to all users;Send a message to all users +IP;IP +URL;URL +Device;Device +Time;Time +Actions;Actions +Change url;Change url +Message;Message +Enter url;Enter url +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you +less than a minute ago;less than a minute ago +The support team has been notified. Please be patient;The support team has been notified. Please be patient +is typing;is typing +Proccessing;Proccessing diff --git a/Moonlight/deleteme_resources/mail/login.html b/Moonlight/deleteme_resources/mail/login.html new file mode 100644 index 00000000..06748d96 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/login.html @@ -0,0 +1,53 @@ + + + + + New moonlight login + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, there is a new login in your moonlight account

+

Here is all the data we collected

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/passwordChange.html b/Moonlight/deleteme_resources/mail/passwordChange.html new file mode 100644 index 00000000..ae3e5a12 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/passwordChange.html @@ -0,0 +1,53 @@ + + + + + Moonlight password change + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, your password has been changed

+

If this was not you please contact us. Also here is the data we collected.

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2023 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/passwordReset.html b/Moonlight/deleteme_resources/mail/passwordReset.html new file mode 100644 index 00000000..9dd066c6 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/passwordReset.html @@ -0,0 +1,54 @@ + + + + + Moonlight password reset + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, your password has been resetted

+

Your new password is: {{Password}}

+

If this was not you please contact us. Also here is the data we collected.

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/register.html b/Moonlight/deleteme_resources/mail/register.html new file mode 100644 index 00000000..3ffbc507 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/register.html @@ -0,0 +1,50 @@ + + + + + Welcome + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, welcome to moonlight

+

We are happy to welcome you in ;)

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/public/images/logo.svg b/Moonlight/deleteme_resources/public/images/logo.svg new file mode 100644 index 00000000..193ebfae --- /dev/null +++ b/Moonlight/deleteme_resources/public/images/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Moonlight/deleteme_resources/public/images/logolong.png b/Moonlight/deleteme_resources/public/images/logolong.png new file mode 100644 index 0000000000000000000000000000000000000000..1494f265b2016cf05d3c58cfe0f77c72be0ef78b GIT binary patch literal 12410 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 U*aKCyJ{bUVQeP$O#f^ji2N?sR&;S4c literal 0 HcmV?d00001 From d671e929070b999102ae9741c649843a5acfd0c4 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 16:32:55 +0200 Subject: [PATCH 34/45] Reverted rename of resources folder NOTE: After closing all branches, DELETE the resources folder because it should not be used any longer --- Moonlight/Moonlight.csproj | 2 +- .../lang/de_de.lang | 0 .../lang/en_us.lang | 0 .../mail/login.html | 0 .../mail/passwordChange.html | 0 .../mail/passwordReset.html | 0 .../mail/register.html | 0 .../public/images/logo.svg | 0 .../public/images/logolong.png | Bin 9 files changed, 1 insertion(+), 1 deletion(-) rename Moonlight/{deleteme_resources => resources}/lang/de_de.lang (100%) rename Moonlight/{deleteme_resources => resources}/lang/en_us.lang (100%) rename Moonlight/{deleteme_resources => resources}/mail/login.html (100%) rename Moonlight/{deleteme_resources => resources}/mail/passwordChange.html (100%) rename Moonlight/{deleteme_resources => resources}/mail/passwordReset.html (100%) rename Moonlight/{deleteme_resources => resources}/mail/register.html (100%) rename Moonlight/{deleteme_resources => resources}/public/images/logo.svg (100%) rename Moonlight/{deleteme_resources => resources}/public/images/logolong.png (100%) diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index e2a9aa4a..1c684c61 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -70,7 +70,7 @@ - + diff --git a/Moonlight/deleteme_resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang similarity index 100% rename from Moonlight/deleteme_resources/lang/de_de.lang rename to Moonlight/resources/lang/de_de.lang diff --git a/Moonlight/deleteme_resources/lang/en_us.lang b/Moonlight/resources/lang/en_us.lang similarity index 100% rename from Moonlight/deleteme_resources/lang/en_us.lang rename to Moonlight/resources/lang/en_us.lang diff --git a/Moonlight/deleteme_resources/mail/login.html b/Moonlight/resources/mail/login.html similarity index 100% rename from Moonlight/deleteme_resources/mail/login.html rename to Moonlight/resources/mail/login.html diff --git a/Moonlight/deleteme_resources/mail/passwordChange.html b/Moonlight/resources/mail/passwordChange.html similarity index 100% rename from Moonlight/deleteme_resources/mail/passwordChange.html rename to Moonlight/resources/mail/passwordChange.html diff --git a/Moonlight/deleteme_resources/mail/passwordReset.html b/Moonlight/resources/mail/passwordReset.html similarity index 100% rename from Moonlight/deleteme_resources/mail/passwordReset.html rename to Moonlight/resources/mail/passwordReset.html diff --git a/Moonlight/deleteme_resources/mail/register.html b/Moonlight/resources/mail/register.html similarity index 100% rename from Moonlight/deleteme_resources/mail/register.html rename to Moonlight/resources/mail/register.html diff --git a/Moonlight/deleteme_resources/public/images/logo.svg b/Moonlight/resources/public/images/logo.svg similarity index 100% rename from Moonlight/deleteme_resources/public/images/logo.svg rename to Moonlight/resources/public/images/logo.svg diff --git a/Moonlight/deleteme_resources/public/images/logolong.png b/Moonlight/resources/public/images/logolong.png similarity index 100% rename from Moonlight/deleteme_resources/public/images/logolong.png rename to Moonlight/resources/public/images/logolong.png From 882ab5cf27f18f4d42e6411e4b5b485c34aed8f4 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 16:36:28 +0200 Subject: [PATCH 35/45] Removed old file manager stuff --- .../Models/Files/Accesses/HostFileAccess.cs | 145 ----- .../Models/Files/Accesses/WingsFileAccess.cs | 205 ------- .../App/Models/Files/FileContextAction.cs | 8 - .../App/Models/Files/FileManagerObject.cs | 10 - Moonlight/App/Models/Files/IFileAccess.cs | 19 - .../FileManagerPartials/FileManager2.razor | 529 ------------------ 6 files changed, 916 deletions(-) delete mode 100644 Moonlight/App/Models/Files/Accesses/HostFileAccess.cs delete mode 100644 Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs delete mode 100644 Moonlight/App/Models/Files/FileContextAction.cs delete mode 100644 Moonlight/App/Models/Files/FileManagerObject.cs delete mode 100644 Moonlight/App/Models/Files/IFileAccess.cs delete mode 100644 Moonlight/Shared/Components/FileManagerPartials/FileManager2.razor diff --git a/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs b/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs deleted file mode 100644 index ef0a66ee..00000000 --- a/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Logging.Net; -using Moonlight.App.Exceptions; -using Moonlight.App.Helpers; - -namespace Moonlight.App.Models.Files.Accesses; - -public class HostFileAccess : IFileAccess -{ - private readonly string BasePath; - private string RealPath => BasePath + Path; - private string Path = "/"; - - public HostFileAccess(string bp) - { - BasePath = bp; - } - - public Task GetDirectoryContent() - { - var x = new List(); - - foreach (var directory in Directory.EnumerateDirectories(RealPath)) - { - x.Add(new () - { - Name = System.IO.Path.GetFileName(directory)!, - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow, - Size = 0, - IsFile = false - }); - } - - foreach (var file in Directory.GetFiles(RealPath)) - { - x.Add(new () - { - Name = System.IO.Path.GetFileName(file)!, - CreatedAt = File.GetCreationTimeUtc(file), - UpdatedAt = File.GetLastWriteTimeUtc(file), - Size = new System.IO.FileInfo(file).Length, - IsFile = File.Exists(file) - }); - } - - return Task.FromResult(x.ToArray()); - } - - public Task ChangeDirectory(string s) - { - var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/"; - x = x.Replace("//", "/"); - Path = x; - - return Task.CompletedTask; - } - - public Task SetDirectory(string s) - { - Path = s; - return Task.CompletedTask; - } - - public Task GoUp() - { - Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", ""); - return Task.CompletedTask; - } - - public Task ReadFile(FileManagerObject fileManagerObject) - { - return Task.FromResult(File.ReadAllText(RealPath + fileManagerObject.Name)); - } - - public Task WriteFile(FileManagerObject fileManagerObject, string content) - { - File.WriteAllText(RealPath + fileManagerObject.Name, content); - return Task.CompletedTask; - } - - public async Task UploadFile(string name, Stream stream, Action? progressUpdated = null) - { - var fs = File.OpenWrite(RealPath + name); - - var dataStream = new StreamProgressHelper(stream) - { - Progress = i => - { - if (progressUpdated != null) - progressUpdated.Invoke(i); - } - }; - - await dataStream.CopyToAsync(fs); - await fs.FlushAsync(); - fs.Close(); - } - - public Task CreateDirectory(string name) - { - Directory.CreateDirectory(RealPath + name); - return Task.CompletedTask; - } - - public Task GetCurrentPath() - { - return Task.FromResult(Path); - } - - public Task GetDownloadStream(FileManagerObject managerObject) - { - var stream = new FileStream(RealPath + managerObject.Name, FileMode.Open, FileAccess.Read); - return Task.FromResult(stream); - } - - public Task GetDownloadUrl(FileManagerObject managerObject) - { - throw new NotImplementedException(); - } - - public Task Delete(FileManagerObject managerObject) - { - if(managerObject.IsFile) - File.Delete(RealPath + managerObject.Name); - else - Directory.Delete(RealPath + managerObject.Name, true); - - return Task.CompletedTask; - } - - public Task Move(FileManagerObject managerObject, string newPath) - { - if(managerObject.IsFile) - File.Move(RealPath + managerObject.Name, BasePath + newPath); - else - Directory.Move(RealPath + managerObject.Name, BasePath + newPath); - - return Task.CompletedTask; - } - - public Task GetLaunchUrl() - { - throw new DisplayException("WinSCP cannot be launched here"); - } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs b/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs deleted file mode 100644 index 1d64ab82..00000000 --- a/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using System.Web; -using JWT.Algorithms; -using JWT.Builder; -using Moonlight.App.Database.Entities; -using Moonlight.App.Helpers; -using Moonlight.App.Models.Wings.Requests; -using Moonlight.App.Models.Wings.Resources; -using RestSharp; - -namespace Moonlight.App.Models.Files.Accesses; - -public class WingsFileAccess : IFileAccess -{ - private readonly WingsApiHelper WingsApiHelper; - private readonly WingsJwtHelper WingsJwtHelper; - private readonly Database.Entities.Node Node; - private readonly Server Server; - private readonly User User; - private readonly string AppUrl; - - private string Path { get; set; } = "/"; - - public WingsFileAccess( - WingsApiHelper wingsApiHelper, - Server server, - User user, - WingsJwtHelper wingsJwtHelper, - string appUrl) - { - WingsApiHelper = wingsApiHelper; - Node = server.Node; - Server = server; - User = user; - WingsJwtHelper = wingsJwtHelper; - AppUrl = appUrl; - } - - public async Task GetDirectoryContent() - { - var res = await WingsApiHelper.Get(Node, - $"api/servers/{Server.Uuid}/files/list-directory?directory={Path}"); - - var x = new List(); - - foreach (var response in res) - { - x.Add(new() - { - Name = response.Name, - Size = response.File ? response.Size : 0, - CreatedAt = response.Created.Date, - IsFile = response.File, - UpdatedAt = response.Modified.Date - }); - } - - return x.ToArray(); - } - - public Task ChangeDirectory(string s) - { - var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/"; - x = x.Replace("//", "/"); - Path = x; - - return Task.CompletedTask; - } - - public Task SetDirectory(string s) - { - Path = s; - return Task.CompletedTask; - } - - public Task GoUp() - { - Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", ""); - return Task.CompletedTask; - } - - public async Task ReadFile(FileManagerObject fileManagerObject) - { - return await WingsApiHelper.GetRaw(Node, - $"api/servers/{Server.Uuid}/files/contents?file={Path}{fileManagerObject.Name}"); - } - - public async Task WriteFile(FileManagerObject fileManagerObject, string content) - { - await WingsApiHelper.PostRaw(Node, - $"api/servers/{Server.Uuid}/files/write?file={Path}{fileManagerObject.Name}", content); - } - - public async Task UploadFile(string name, Stream dataStream, Action onProgress) - { - var token = WingsJwtHelper.Generate(Node.Token, - claims => { claims.Add("server_uuid", Server.Uuid.ToString()); }); - - var client = new RestClient(); - var request = new RestRequest(); - - if (Node.Ssl) - request.Resource = $"https://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}"; - else - request.Resource = $"http://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}"; - - request.AddParameter("name", "files"); - request.AddParameter("filename", name); - request.AddHeader("Content-Type", "multipart/form-data"); - request.AddHeader("Origin", AppUrl); - request.AddFile("files", () => - { - return new StreamProgressHelper(dataStream) - { - Progress = i => - { - if (onProgress != null) - onProgress.Invoke(i); - } - }; - }, name); - - await client.ExecutePostAsync(request); - - client.Dispose(); - dataStream.Close(); - } - - public async Task CreateDirectory(string name) - { - await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/create-directory", - new CreateDirectory() - { - Name = name, - Path = Path - }); - } - - public Task GetCurrentPath() - { - return Task.FromResult(Path); - } - - public Task GetDownloadStream(FileManagerObject managerObject) - { - throw new NotImplementedException(); - } - - public Task GetDownloadUrl(FileManagerObject managerObject) - { - var token = WingsJwtHelper.Generate(Node.Token, claims => - { - claims.Add("server_uuid", Server.Uuid.ToString()); - claims.Add("file_path", HttpUtility.UrlDecode(Path + "/" + managerObject.Name)); - }); - - if (Node.Ssl) - { - return Task.FromResult( - $"https://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}" - ); - } - else - { - return Task.FromResult( - $"http://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}" - ); - } - } - - public async Task Delete(FileManagerObject managerObject) - { - await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFiles() - { - Root = Path, - Files = new() - { - managerObject.Name - } - }); - } - - public async Task Move(FileManagerObject managerObject, string newPath) - { - await WingsApiHelper.Put(Node, $"api/servers/{Server.Uuid}/files/rename", new RenameFiles() - { - Root = "/", - Files = new[] - { - new RenameFilesData() - { - From = Path + managerObject.Name, - To = newPath - } - } - }); - } - - public Task GetLaunchUrl() - { - return Task.FromResult( - $"sftp://{User.Id}.{StringHelper.IntToStringWithLeadingZeros(Server.Id, 8)}@{Node.Fqdn}:{Node.SftpPort}"); - } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/FileContextAction.cs b/Moonlight/App/Models/Files/FileContextAction.cs deleted file mode 100644 index 7570013a..00000000 --- a/Moonlight/App/Models/Files/FileContextAction.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public class FileContextAction -{ - public string Id { get; set; } - public string Name { get; set; } - public Action Action { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/FileManagerObject.cs b/Moonlight/App/Models/Files/FileManagerObject.cs deleted file mode 100644 index 063d5561..00000000 --- a/Moonlight/App/Models/Files/FileManagerObject.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public class FileManagerObject -{ - public string Name { get; set; } - public bool IsFile { get; set; } - public long Size { get; set; } - public DateTime CreatedAt { get; set; } - public DateTime UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/IFileAccess.cs b/Moonlight/App/Models/Files/IFileAccess.cs deleted file mode 100644 index 6792d402..00000000 --- a/Moonlight/App/Models/Files/IFileAccess.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public interface IFileAccess -{ - public Task GetDirectoryContent(); - public Task ChangeDirectory(string s); - public Task SetDirectory(string s); - public Task GoUp(); - public Task ReadFile(FileManagerObject fileManagerObject); - public Task WriteFile(FileManagerObject fileManagerObject, string content); - public Task UploadFile(string name, Stream stream, Action progressUpdated); - public Task CreateDirectory(string name); - public Task GetCurrentPath(); - public Task GetDownloadStream(FileManagerObject managerObject); - public Task GetDownloadUrl(FileManagerObject managerObject); - public Task Delete(FileManagerObject managerObject); - public Task Move(FileManagerObject managerObject, string newPath); - public Task GetLaunchUrl(); -} \ No newline at end of file diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileManager2.razor b/Moonlight/Shared/Components/FileManagerPartials/FileManager2.razor deleted file mode 100644 index cf8c13b6..00000000 --- a/Moonlight/Shared/Components/FileManagerPartials/FileManager2.razor +++ /dev/null @@ -1,529 +0,0 @@ -@using Moonlight.App.Helpers -@using BlazorContextMenu -@using BlazorDownloadFile -@using Logging.Net -@using Moonlight.App.Models.Files -@using Moonlight.App.Services -@using Moonlight.App.Services.Interop - -@inject AlertService AlertService -@inject ToastService ToastService -@inject NavigationManager NavigationManager -@inject SmartTranslateService TranslationService -@inject IBlazorDownloadFileService FileService - -
-@if (Editing == null) -{ -
-
-
- - - - - - - -
-
-
- @if (SelectedFiles.Count == 0) - { -
- - - - -
- } - else - { -
-
- - @(SelectedFiles.Count) - - Selected -
- - -
- } -
-
-
-
-
-
- @{ - var vx = "/"; - } - / - - - - - - @{ - var cp = "/"; - var lp = "/"; - var pathParts = CurrentPath.Replace("\\", "/").Split('/', StringSplitOptions.RemoveEmptyEntries); - foreach (var path in pathParts) - { - lp = cp; - @(path) - - - - - - - cp += path + "/"; - } - } -
-
-
- -
-} -else -{ - if (Loading) - { -
- - Loading - -
- } - else - { - - } -} -
- - - Rename - Move - Archive - Unarchive - Download - Delete - - -@code -{ - [Parameter] - public IFileAccess FileAccess { get; set; } - - // Data - - private List SelectedFiles { get; set; } = new(); - private List Objects { get; set; } = new(); - private string CurrentPath = ""; - - // Search - private string SearchValue = ""; - - private string Search - { - get { return SearchValue; } - set - { - SearchValue = value; - InvokeAsync(StateHasChanged); - } - } - - // States - private bool Loading = false; - - // States - Editor - private FileManagerObject? Editing = null; - private string InitialEditorData = ""; - private string Language = "plaintext"; - - // States - File Upload - private bool Uploading = false; - private int Percent = 0; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await RefreshActive(); - } - } - - private async Task RefreshActive() - { - Loading = true; - await InvokeAsync(StateHasChanged); - - await Refresh(false); - - Loading = false; - await InvokeAsync(StateHasChanged); - } - - private async Task Refresh(bool rerender = true) - { - SelectedFiles.Clear(); - Objects.Clear(); - CurrentPath = await FileAccess.GetCurrentPath(); - - var data = await FileAccess.GetDirectoryContent(); - Objects = data.ToList(); - - if (rerender) - await InvokeAsync(StateHasChanged); - } - - private async Task CdPath(string path) - { - await FileAccess.ChangeDirectory(path); - await RefreshActive(); - } - - private async Task SetPath(string path) - { - await FileAccess.SetDirectory(path); - await RefreshActive(); - } - - private async Task OnContextMenuClick(ItemClickEventArgs e) - { - var data = e.Data as FileManagerObject; - - if (data == null) - return; - - switch (e.MenuItem.Id) - { - case "delete": - await FileAccess.Delete(data); - break; - case "download": - if (data.IsFile) - { - try - { - var stream = await FileAccess.GetDownloadStream(data); - await ToastService.Info(TranslationService.Translate("Starting download")); - await FileService.AddBuffer(stream); - await FileService.DownloadBinaryBuffers(data.Name); - } - catch (NotImplementedException) - { - try - { - var url = await FileAccess.GetDownloadUrl(data); - NavigationManager.NavigateTo(url, true); - await ToastService.Info(TranslationService.Translate("Starting download")); - } - catch (Exception exception) - { - await ToastService.Error(TranslationService.Translate("Error starting download")); - Logger.Error("Error downloading file"); - Logger.Error(exception.Message); - } - } - catch (Exception exception) - { - await ToastService.Error(TranslationService.Translate("Error starting download")); - Logger.Error("Error downloading file stream"); - Logger.Error(exception.Message); - } - } - break; - case "rename": - var newName = await AlertService.Text(TranslationService.Translate("Rename"), TranslationService.Translate("Enter a new name"), data.Name); - var path = await FileAccess.GetCurrentPath(); - await FileAccess.Move(data, path + "/" + newName); - break; - } - - await Refresh(false); - } - - private async Task OnFileToggle(ChangeEventArgs obj, FileManagerObject o) - { - if ((bool)obj.Value) - { - if (SelectedFiles.Contains(o)) - return; - - SelectedFiles.Add(o); - await InvokeAsync(StateHasChanged); - } - else - { - if (!SelectedFiles.Contains(o)) - return; - - SelectedFiles.Remove(o); - await InvokeAsync(StateHasChanged); - } - } - - private async Task OnAllFileToggle(ChangeEventArgs obj) - { - if ((bool)obj.Value) - { - foreach (var o in Objects) - { - if (SelectedFiles.Contains(o)) - continue; - - SelectedFiles.Add(o); - } - - await InvokeAsync(StateHasChanged); - } - else - { - foreach (var o in Objects) - { - if (!SelectedFiles.Contains(o)) - continue; - - SelectedFiles.Remove(o); - } - - - await InvokeAsync(StateHasChanged); - } - } - - private async Task CreateFolder() - { - var name = await AlertService.Text(TranslationService.Translate("Create a new folder"), TranslationService.Translate("Enter a name"), ""); - - if (string.IsNullOrEmpty(name)) - return; - - await FileAccess.CreateDirectory(name); - await Refresh(); - } - - private async void SaveFile(string data) - { - if (Editing == null) - return; - - await FileAccess.WriteFile(Editing, data); - Editing = null; - await Refresh(); - } - - private async Task OpenFile(FileManagerObject o) - { - Editing = o; - Loading = true; - await InvokeAsync(StateHasChanged); - - InitialEditorData = await FileAccess.ReadFile(Editing); - Language = MonacoTypeHelper.GetEditorType(Editing.Name); - - Loading = false; - await InvokeAsync(StateHasChanged); - } - - private async void CloseFile() - { - Editing = null; - await InvokeAsync(StateHasChanged); - } - - private async Task Launch() - { - NavigationManager.NavigateTo(await FileAccess.GetLaunchUrl()); - } - - private async Task OnFileChanged(InputFileChangeEventArgs arg) - { - Uploading = true; - await InvokeAsync(StateHasChanged); - - foreach (var browserFile in arg.GetMultipleFiles()) - { - if (browserFile.Size < 1024 * 1024 * 100) - { - Percent = 0; - - try - { - await FileAccess.UploadFile( - browserFile.Name, - browserFile.OpenReadStream(1024 * 1024 * 100), - async (i) => - { - Percent = i; - - Task.Run(() => { InvokeAsync(StateHasChanged); }); - }); - - await Refresh(); - } - catch (Exception e) - { - await ToastService.Error(TranslationService.Translate("An unknown error occured while uploading a file")); - Logger.Error("Error uploading file"); - Logger.Error(e); - } - } - else - { - await ToastService.Error(TranslationService.Translate("The uploaded file should not be bigger than 100MB")); - } - } - - Uploading = false; - await InvokeAsync(StateHasChanged); - - await ToastService.Success(TranslationService.Translate("File upload complete")); - } -} \ No newline at end of file From 08e4a9df228fd09415044310bdcf733fa0701dc9 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 16:43:30 +0200 Subject: [PATCH 36/45] Fixed is empty method --- Moonlight/App/Services/StorageService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moonlight/App/Services/StorageService.cs b/Moonlight/App/Services/StorageService.cs index 455e5329..f9474581 100644 --- a/Moonlight/App/Services/StorageService.cs +++ b/Moonlight/App/Services/StorageService.cs @@ -39,7 +39,7 @@ public class StorageService private bool IsEmpty(string path) { - return !Directory.EnumerateFiles(path).Any(); + return !Directory.EnumerateFileSystemEntries(path).Any(); } private static void CopyFilesRecursively(string sourcePath, string targetPath) { From e6ede7efbb3af2aba8193551ed42a9ea180521f2 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:43:42 +0200 Subject: [PATCH 37/45] fixed design of delete button --- Moonlight/Shared/Components/Forms/DeleteButton.razor | 11 +++++++---- Moonlight/Shared/Views/Admin/Domains/Index.razor | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Moonlight/Shared/Components/Forms/DeleteButton.razor b/Moonlight/Shared/Components/Forms/DeleteButton.razor index 7cd719a2..e77ef226 100644 --- a/Moonlight/Shared/Components/Forms/DeleteButton.razor +++ b/Moonlight/Shared/Components/Forms/DeleteButton.razor @@ -6,14 +6,14 @@ @if (!Working) { - } else { - } @@ -27,6 +27,9 @@ else [Parameter] public bool Confirm { get; set; } = false; + [Parameter] + public string AdditionalCssClasses { get; set; } = ""; + private async Task Do() { Working = true; diff --git a/Moonlight/Shared/Views/Admin/Domains/Index.razor b/Moonlight/Shared/Views/Admin/Domains/Index.razor index 09c0d307..406aab1f 100644 --- a/Moonlight/Shared/Views/Admin/Domains/Index.razor +++ b/Moonlight/Shared/Views/Admin/Domains/Index.razor @@ -49,6 +49,7 @@ From c750c180a78a103a5617ec4ac2f62136d6cd2ae6 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Thu, 13 Apr 2023 17:34:10 +0200 Subject: [PATCH 38/45] redesigned shared domains screen | split in view/add --- .../Shared/Views/Admin/Domains/Shared.razor | 126 ------------------ .../Views/Admin/Domains/Shared/Index.razor | 83 ++++++++++++ .../Views/Admin/Domains/Shared/New.razor | 83 ++++++++++++ 3 files changed, 166 insertions(+), 126 deletions(-) delete mode 100644 Moonlight/Shared/Views/Admin/Domains/Shared.razor create mode 100644 Moonlight/Shared/Views/Admin/Domains/Shared/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Domains/Shared/New.razor diff --git a/Moonlight/Shared/Views/Admin/Domains/Shared.razor b/Moonlight/Shared/Views/Admin/Domains/Shared.razor deleted file mode 100644 index 95b270c9..00000000 --- a/Moonlight/Shared/Views/Admin/Domains/Shared.razor +++ /dev/null @@ -1,126 +0,0 @@ -@page "/admin/domains/shared" -@using Moonlight.App.Repositories.Domains -@using Moonlight.App.Services -@using CloudFlare.Client.Api.Zones -@using Moonlight.App.Database.Entities -@using Moonlight.App.Services.Interop - -@inject SharedDomainRepository SharedDomainRepository -@inject SmartTranslateService SmartTranslateService -@inject DomainService DomainService -@inject AlertService AlertService -@inject ToastService ToastService - - - -
-
-
- - -
- - -
-
- @foreach (var sharedDomain in SharedDomains) - { -
-
-
-
- -
-
- @(sharedDomain.Name) -
-
- @(sharedDomain.Id) -
-
- - -
-
-
-
- } -
-
- -@code -{ - private string DomainId = ""; - private Zone[] Zones; - private SharedDomain[] SharedDomains; - - private LazyLoader LazyLoader; - - private async Task Load(LazyLoader lazyLoader) - { - Zones = await DomainService.GetAvailableDomains(); - - SharedDomains = SharedDomainRepository.Get().ToArray(); - } - - private async Task Add() - { - if(string.IsNullOrEmpty(DomainId)) - return; - - var domain = Zones.First(x => x.Id == DomainId); - - var sd = new SharedDomain() - { - CloudflareId = domain.Id, - Name = domain.Name - }; - - SharedDomainRepository.Add(sd); - await ToastService.Success(SmartTranslateService.Translate("Shared domain successfully added")); - - await LazyLoader.Reload(); - } - - private async Task Delete(SharedDomain sharedDomain) - { - try - { - SharedDomainRepository.Delete(sharedDomain); - await ToastService.Success(SmartTranslateService.Translate("Shared domain successfully deleted")); - - await LazyLoader.Reload(); - } - catch (Exception e) - { - //TODO: Add check if any domains are left - - await AlertService.Error( - SmartTranslateService.Translate("Error"), - SmartTranslateService.Translate("Something went wrong. Are any domains associated with this shared domain still there?") - ); - } - } -} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Domains/Shared/Index.razor b/Moonlight/Shared/Views/Admin/Domains/Shared/Index.razor new file mode 100644 index 00000000..86a1d1c6 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Domains/Shared/Index.razor @@ -0,0 +1,83 @@ +@page "/admin/domains/shared" +@using Moonlight.App.Repositories.Domains +@using Moonlight.App.Services +@using CloudFlare.Client.Api.Zones +@using Moonlight.App.Database.Entities +@using Moonlight.App.Services.Interop +@using BlazorTable + +@inject SharedDomainRepository SharedDomainRepository +@inject SmartTranslateService SmartTranslateService +@inject DomainService DomainService +@inject AlertService AlertService +@inject ToastService ToastService + + + +
+
+

+ + Shared domains + +

+ +
+
+ + + + + + +
+
+
+
+
+ +@code +{ + private string DomainId = ""; + private Zone[] Zones; + private SharedDomain[] SharedDomains; + + private LazyLoader LazyLoader; + + private async Task Load(LazyLoader lazyLoader) + { + Zones = await DomainService.GetAvailableDomains(); + + SharedDomains = SharedDomainRepository.Get().ToArray(); + } + + private async Task Delete(SharedDomain sharedDomain) + { + try + { + SharedDomainRepository.Delete(sharedDomain); + await ToastService.Success(SmartTranslateService.Translate("Shared domain successfully deleted")); + + await LazyLoader.Reload(); + } + catch (Exception e) + { + //TODO: Add check if any domains are left + + await AlertService.Error( + SmartTranslateService.Translate("Error"), + SmartTranslateService.Translate("Something went wrong. Are any domains associated with this shared domain still there?") + ); + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Domains/Shared/New.razor b/Moonlight/Shared/Views/Admin/Domains/Shared/New.razor new file mode 100644 index 00000000..6d48f93f --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Domains/Shared/New.razor @@ -0,0 +1,83 @@ +@page "/admin/domains/shared/new" + +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Moonlight.App.Repositories.Domains +@using CloudFlare.Client.Api.Zones +@using Moonlight.App.Database.Entities + +@inject SharedDomainRepository SharedDomainRepository +@inject SmartTranslateService SmartTranslateService +@inject DomainService DomainService +@inject ToastService ToastService +@inject NavigationManager NavigationManager + + +
+
+
+
+ + +
+
+ + +
+
+
+
+
+ +@code { + private string DomainId = ""; + private Zone[] Zones; + private SharedDomain[] SharedDomains; + + private LazyLoader LazyLoader; + + private async Task Load(LazyLoader lazyLoader) + { + Zones = await DomainService.GetAvailableDomains(); + + SharedDomains = SharedDomainRepository.Get().ToArray(); + } + + private async Task Add() + { + if(string.IsNullOrEmpty(DomainId)) + return; + + var domain = Zones.First(x => x.Id == DomainId); + + var sd = new SharedDomain() + { + CloudflareId = domain.Id, + Name = domain.Name + }; + + SharedDomainRepository.Add(sd); + await ToastService.Success(SmartTranslateService.Translate("Shared domain successfully added")); + + NavigationManager.NavigateTo("/admin/domains/shared"); + } +} \ No newline at end of file From 729e91c20c9907effb649779a3ca21eab32e2ddc Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 14 Apr 2023 11:59:22 +0200 Subject: [PATCH 39/45] Fixed some stuff like allocation, wings oom killer option --- Moonlight/App/Database/Entities/Server.cs | 2 +- ...426_MadeMainAllocationNullable.Designer.cs | 995 ++++++++++++++++++ ...230413150426_MadeMainAllocationNullable.cs | 59 ++ .../Migrations/DataContextModelSnapshot.cs | 6 +- Moonlight/App/Helpers/WingsServerConverter.cs | 1 + .../App/Http/Resources/Wings/WingsServer.cs | 3 + 6 files changed, 1061 insertions(+), 5 deletions(-) create mode 100644 Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs diff --git a/Moonlight/App/Database/Entities/Server.cs b/Moonlight/App/Database/Entities/Server.cs index cad9ced9..aa55d817 100644 --- a/Moonlight/App/Database/Entities/Server.cs +++ b/Moonlight/App/Database/Entities/Server.cs @@ -17,7 +17,7 @@ public class Server public List Variables { get; set; } = new(); public List Backups { get; set; } = new(); public List Allocations { get; set; } = new(); - public NodeAllocation MainAllocation { get; set; } = null!; + public NodeAllocation? MainAllocation { get; set; } = null; public Node Node { get; set; } = null!; public User Owner { get; set; } = null!; public bool IsCleanupException { get; set; } = false; diff --git a/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs new file mode 100644 index 00000000..0eb52201 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs @@ -0,0 +1,995 @@ +// +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("20230413150426_MadeMainAllocationNullable")] + partial class MadeMainAllocationNullable + { + /// + 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.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.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + 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.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.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.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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs new file mode 100644 index 00000000..e6d79d8a --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class MadeMainAllocationNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers"); + + migrationBuilder.AlterColumn( + name: "MainAllocationId", + table: "Servers", + type: "int", + nullable: true, + oldClrType: typeof(int), + oldType: "int"); + + migrationBuilder.AddForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers", + column: "MainAllocationId", + principalTable: "NodeAllocations", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers"); + + migrationBuilder.AlterColumn( + name: "MainAllocationId", + table: "Servers", + type: "int", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "int", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers", + column: "MainAllocationId", + principalTable: "NodeAllocations", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index dbe9e502..fdc16361 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -479,7 +479,7 @@ namespace Moonlight.App.Database.Migrations b.Property("IsCleanupException") .HasColumnType("tinyint(1)"); - b.Property("MainAllocationId") + b.Property("MainAllocationId") .HasColumnType("int"); b.Property("Memory") @@ -886,9 +886,7 @@ namespace Moonlight.App.Database.Migrations b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") .WithMany() - .HasForeignKey("MainAllocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("MainAllocationId"); b.HasOne("Moonlight.App.Database.Entities.Node", "Node") .WithMany() diff --git a/Moonlight/App/Helpers/WingsServerConverter.cs b/Moonlight/App/Helpers/WingsServerConverter.cs index f1ebbc29..4f22303c 100644 --- a/Moonlight/App/Helpers/WingsServerConverter.cs +++ b/Moonlight/App/Helpers/WingsServerConverter.cs @@ -53,6 +53,7 @@ public class WingsServerConverter wingsServer.Settings.Build.Io_Weight = 500; wingsServer.Settings.Build.Memory_Limit = server.Memory; wingsServer.Settings.Build.Oom_Disabled = true; + wingsServer.Settings.Build.Oom_Killer = false; var image = ImageRepository .Get() diff --git a/Moonlight/App/Http/Resources/Wings/WingsServer.cs b/Moonlight/App/Http/Resources/Wings/WingsServer.cs index d55a81e9..8dafcd7e 100644 --- a/Moonlight/App/Http/Resources/Wings/WingsServer.cs +++ b/Moonlight/App/Http/Resources/Wings/WingsServer.cs @@ -130,6 +130,9 @@ public class WingsServer [JsonPropertyName("oom_disabled")] public bool Oom_Disabled { get; set; } + + [JsonPropertyName("oom_killer")] + public bool Oom_Killer { get; set; } } public class WingsServerContainer From b6b9a4f8f11d1f7dabbc5edda309e6a109f7f35e Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 14 Apr 2023 13:06:55 +0200 Subject: [PATCH 40/45] Removed legacy image tags --- Moonlight/App/Database/DataContext.cs | 1 - Moonlight/App/Database/Entities/ImageTag.cs | 7 - ...0230414110513_RemovedImageTags.Designer.cs | 980 ++++++++++++++++++ .../20230414110513_RemovedImageTags.cs | 37 + .../Migrations/DataContextModelSnapshot.cs | 15 - 5 files changed, 1017 insertions(+), 23 deletions(-) delete mode 100644 Moonlight/App/Database/Entities/ImageTag.cs create mode 100644 Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 4b7be797..dc3e77de 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -18,7 +18,6 @@ public class DataContext : DbContext public DbSet DockerImages { get; set; } public DbSet Images { get; set; } - public DbSet ImageTags { get; set; } public DbSet ImageVariables { get; set; } public DbSet Nodes { get; set; } public DbSet NodeAllocations { get; set; } diff --git a/Moonlight/App/Database/Entities/ImageTag.cs b/Moonlight/App/Database/Entities/ImageTag.cs deleted file mode 100644 index 7f3063fb..00000000 --- a/Moonlight/App/Database/Entities/ImageTag.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Moonlight.App.Database.Entities; - -public class ImageTag -{ - public int Id { get; set; } - public string Name { get; set; } = ""; -} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs new file mode 100644 index 00000000..fafeeafe --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs @@ -0,0 +1,980 @@ +// +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("20230414110513_RemovedImageTags")] + partial class RemovedImageTags + { + /// + 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.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.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.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.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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs new file mode 100644 index 00000000..2001c98f --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class RemovedImageTags : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ImageTags"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ImageTags", + 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") + }, + constraints: table => + { + table.PrimaryKey("PK_ImageTags", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index fdc16361..7a0da1a7 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -153,21 +153,6 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Images"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("ImageTags"); - }); - modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => { b.Property("Id") From 7de723d0b81ee3bd78f15f514e3d7c79c5a460ff Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 14 Apr 2023 13:59:35 +0200 Subject: [PATCH 41/45] Fixed server deletion and creation allocation bugs --- .../Api/Remote/ServersController.cs | 10 ++++- Moonlight/App/Services/ServerService.cs | 39 ++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs index 9ea1e8fc..1136bcae 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs @@ -138,7 +138,15 @@ public class ServersController : Controller await MessageService.Emit($"wings.{node.Id}.serverfetch", server); - return Converter.FromServer(server); + try //TODO: Remove + { + return Converter.FromServer(server); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } [HttpGet("{uuid}/install")] diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index ccde6026..01bc2ee0 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -201,7 +201,8 @@ public class ServerService { x.Add(server.Uuid); x.Add(backup.Uuid); - }); + } + ); } public async Task DownloadBackup(Server s, ServerBackup serverBackup) @@ -262,10 +263,15 @@ public class ServerService node = NodeRepository .Get() .Include(x => x.Allocations) - .First(x => x.Id == n.Id); + .First(); //TODO: Add smart deploy maybe } else - node = n; + { + node = NodeRepository + .Get() + .Include(x => x.Allocations) + .First(x => x.Id == n.Id); + } NodeAllocation[] freeAllocations; @@ -339,7 +345,7 @@ public class ServerService x.Add(node.Id); }); - ServerRepository.Delete(newServerData); + ServerRepository.Delete(newServerData); //TODO Remove unsinged table stuff throw new DisplayException("Error creating server on wings"); } @@ -388,9 +394,32 @@ public class ServerService { var server = EnsureNodeData(s); + var backups = await GetBackups(server); + + foreach (var backup in backups) + { + try + { + await DeleteBackup(server, backup); + } + catch (Exception) + { + // ignored + } + } + await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null); - ServerRepository.Delete(s); + //TODO: Fix empty data models + + server.Allocations = new(); + server.MainAllocation = null; + server.Variables = new(); + server.Backups = new(); + + ServerRepository.Update(server); + + ServerRepository.Delete(server); } public async Task IsHostUp(Server s) From 1d4ae8d26d0f89293e5b57b5e33eb0ba00b99fee Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 14 Apr 2023 15:02:17 +0200 Subject: [PATCH 42/45] Fixed mail template storage access, added docker image default resources --- Moonlight/App/Services/MailService.cs | 5 +++-- Moonlight/Dockerfile | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Moonlight/App/Services/MailService.cs b/Moonlight/App/Services/MailService.cs index 38482a2b..26764603 100644 --- a/Moonlight/App/Services/MailService.cs +++ b/Moonlight/App/Services/MailService.cs @@ -3,6 +3,7 @@ using System.Net.Mail; using Logging.Net; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; +using Moonlight.App.Helpers; namespace Moonlight.App.Services; @@ -31,13 +32,13 @@ public class MailService Action> values ) { - if (!File.Exists($"resources/mail/{name}.html")) + if (!File.Exists(PathBuilder.File("storage", "resources", "mail", $"{name}.html"))) { Logger.Warn($"Mail template '{name}' not found. Make sure to place one in the resources folder"); throw new DisplayException("Mail template not found"); } - var rawHtml = await File.ReadAllTextAsync($"resources/mail/{name}.html"); + var rawHtml = await File.ReadAllTextAsync(PathBuilder.File("storage", "resources", "mail", $"{name}.html")); var val = new Dictionary(); values.Invoke(val); diff --git a/Moonlight/Dockerfile b/Moonlight/Dockerfile index 4fecfff7..f99a7334 100644 --- a/Moonlight/Dockerfile +++ b/Moonlight/Dockerfile @@ -19,6 +19,7 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -RUN mkdir /app/storage +RUN mkdir -p /app/storage RUN rm -r /app/storage/* +COPY "Moonlight/defaultstorage" "/app/defaultstorage" ENTRYPOINT ["dotnet", "Moonlight.dll"] \ No newline at end of file From 41032d30b08fe976b0eea28e5d5cbcf539c116ce Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:13:41 +0200 Subject: [PATCH 43/45] lang files --- .../defaultstorage/resources/lang/de_de.lang | Bin 20706 -> 45980 bytes .../defaultstorage/resources/lang/en_us.lang | 567 ++++++++++++++++-- 2 files changed, 524 insertions(+), 43 deletions(-) diff --git a/Moonlight/defaultstorage/resources/lang/de_de.lang b/Moonlight/defaultstorage/resources/lang/de_de.lang index eb4bb032a03edbb17622fe2a3dd876982772fda5..4f127265e71718fb4753f2f4e933a429cab840b3 100644 GIT binary patch literal 45980 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 literal 20706 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 diff --git a/Moonlight/defaultstorage/resources/lang/en_us.lang b/Moonlight/defaultstorage/resources/lang/en_us.lang index 2e2e1fcb..eb4bb032 100644 --- a/Moonlight/defaultstorage/resources/lang/en_us.lang +++ b/Moonlight/defaultstorage/resources/lang/en_us.lang @@ -2,30 +2,93 @@ Open support;Open support About us;About us Imprint;Imprint Privacy;Privacy -Create;Create -Server;Server -Domain;Domain -Website;Website Login;Login Register;Register -Email;Email -Password;Password +Insert brand name...;Insert brand name... +Save and continue;Save and continue +Saving;Saving +Configure basics;Configure basics +Brand name;Brand name +test;test +Insert first name...;Insert first name... +Insert last name...;Insert last name... +Insert email address...;Insert email address... +Add;Add +Adding...;Adding... +Add admin accounts;Add admin accounts +First name;First name +Last name;Last name +Email address;Email address +Enter password;Enter password +Next;Next +Back;Back +Configure features;Configure features +Support chat;Support chat +Finish;Finish +Finalize installation;Finalize installation +Moonlight basic settings successfully configured;Moonlight basic settings successfully configured +Ooops. This page is crashed;Ooops. This page is crashed +This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page;This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page +Setup complete;Setup complete +It looks like this moonlight instance is ready to go;It looks like this moonlight instance is ready to go +User successfully created;User successfully created +Ooops. Your moonlight client is crashed;Ooops. Your moonlight client is crashed +This error has been reported to the moonlight team;This error has been reported to the moonlight team Sign In;Sign In Sign in to start with moonlight;Sign in to start with moonlight Sign in with Discord;Sign in with Discord -Sign in with Google;Sign in with Google Or with email;Or with email Forgot password?;Forgot password? Sign-in;Sign-in Not registered yet?;Not registered yet? Sign up;Sign up -Profile;Profile +Authenticating;Authenticating +Sign in with Google;Sign in with Google +Working;Working +Error;Error +Email and password combination not found;Email and password combination not found +Email;Email +Password;Password +Account settings;Account settings Logout;Logout Dashboard;Dashboard +Order;Order +Website;Website +Database;Database +Domain;Domain Servers;Servers Websites;Websites +Databases;Databases Domains;Domains Changelog;Changelog +Firstname;Firstname +Lastname;Lastname +Repeat password;Repeat password +Sign Up;Sign Up +Sign up to start with moonlight;Sign up to start with moonlight +Sign up with Discord;Sign up with Discord +Sign up with Google;Sign up with Google +Sign-up;Sign-up +Already registered?;Already registered? +Sign in;Sign in +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a database;Create a database +A quick way to store your data and manage it from all around the world;A quick way to store your data and manage it from all around the world +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your databases;Manage your databases +Insert, delete and update the data in your databases;Insert, delete and update the data in your databases +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records Admin;Admin System;System Overview;Overview @@ -33,54 +96,472 @@ Manager;Manager Cleanup;Cleanup Nodes;Nodes Images;Images +aaPanel;aaPanel Users;Users -Shared domains;Shared domains Support;Support -Subscriptions;Subscriptions Statistics;Statistics -Create something new;Create something new -Create a gameserver;Create a gameserver -A new gameserver in just a few minutes;A new gameserver in just a few minutes -Create a website;Create a website -Make your own websites with a webspace;Make your own websites with a webspace -Create a domain;Create a domain -Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain -Manage your services;Manage your services -Manage your gameservers;Manage your gameservers -Adjust your gameservers;Adjust your gameservers -Manage your websites;Manage your websites -Modify the content of your websites;Modify the content of your websites -Manage your domains;Manage your domains -Add, edit and delete dns records;Add, edit and delete dns records -New server;New server +No nodes found. Start with adding a new node;No nodes found. Start with adding a new node +Nodename;Nodename +FQDN;FQDN +Create;Create +Creating;Creating +Http port;Http port +Sftp port;Sftp port +Moonlight daemon port;Moonlight daemon port +SSL;SSL +CPU Usage;CPU Usage +In %;In % +Memory;Memory +Used / Available memory;Used / Available memory +Storage;Storage +Available storage;Available storage +Add a new node;Add a new node +Delete;Delete +Deleting;Deleting +Edit;Edit +Token Id;Token Id +Token;Token +Save;Save +Setup;Setup +Open a ssh connection to your node and enter;Open a ssh connection to your node and enter +and paste the config below. Then press STRG+O and STRG+X to save;and paste the config below. Then press STRG+O and STRG+X to save +Before configuring this node, install the daemon;Before configuring this node, install the daemon +Delete this node?;Delete this node? +Do you really want to delete this node;Do you really want to delete this node +Yes;Yes +No;No +Status;Status +Adding;Adding +Port;Port Id;Id +Manage;Manage +Create new server;Create new server +No servers found;No servers found +Server name;Server name +Cpu cores;Cpu cores +Disk;Disk +Image;Image +Override startup;Override startup +Docker image;Docker image +CPU Cores (100% = 1 Core);CPU Cores (100% = 1 Core) +Server successfully created;Server successfully created Name;Name Cores;Cores -Memory;Memory -Disk;Disk Owner;Owner -Manage;Manage -Node offline;Node offline -The node the server is running on is currently offline;The node the server is running on is currently offline -Sessions;Sessions -New user;New user -First name;First name -Last name;Last name -Created at;Created at +Value;Value +An unknown error occured;An unknown error occured +No allocation found;No allocation found +Identifier;Identifier +UuidIdentifier;UuidIdentifier +Override startup command;Override startup command +Loading;Loading +Offline;Offline +Connecting;Connecting +Start;Start +Restart;Restart +Stop;Stop +Shared IP;Shared IP +Server ID;Server ID +Cpu;Cpu +Console;Console +Files;Files +Backups;Backups +Network;Network +Plugins;Plugins +Settings;Settings +Enter command;Enter command +Execute;Execute +Checking disk space;Checking disk space +Updating config files;Updating config files +Checking file permissions;Checking file permissions +Downloading server image;Downloading server image +Downloaded server image;Downloaded server image +Starting;Starting +Online;Online +Kill;Kill +Stopping;Stopping +Search files and folders;Search files and folders +Launch WinSCP;Launch WinSCP +New folder;New folder +Upload;Upload +File name;File name +File size;File size +Last modified;Last modified +Cancel;Cancel +Canceling;Canceling +Running;Running +Loading backups;Loading backups +Started backup creation;Started backup creation +Backup is going to be created;Backup is going to be created +Rename;Rename +Move;Move +Archive;Archive +Unarchive;Unarchive +Download;Download +Starting download;Starting download +Backup successfully created;Backup successfully created +Restore;Restore +Copy url;Copy url +Backup deletion started;Backup deletion started +Backup successfully deleted;Backup successfully deleted +Primary;Primary +This feature is currently not available;This feature is currently not available +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you + minutes ago; minutes ago +just now;just now +less than a minute ago;less than a minute ago +1 hour ago;1 hour ago +1 minute ago;1 minute ago +Failed;Failed + hours ago; hours ago +Open tickets;Open tickets +Actions;Actions +No support ticket is currently open;No support ticket is currently open +User information;User information +Close ticket;Close ticket +Closing;Closing +The support team has been notified. Please be patient;The support team has been notified. Please be patient +The ticket is now closed. Type a message to open it again;The ticket is now closed. Type a message to open it again +1 day ago;1 day ago +is typing;is typing +are typing;are typing +No domains available;No domains available +Shared domains;Shared domains +Shared domain;Shared domain +Shared domain successfully deleted;Shared domain successfully deleted +Shared domain successfully added;Shared domain successfully added +Domain name;Domain name +DNS records for;DNS records for +Fetching dns records;Fetching dns records +No dns records found;No dns records found +Content;Content +Priority;Priority +Ttl;Ttl +Enable cloudflare proxy;Enable cloudflare proxy +CF Proxy;CF Proxy + days ago; days ago +Cancle;Cancle +An unexpected error occured;An unexpected error occured +Testy;Testy +Error from cloudflare api;Error from cloudflare api +Profile;Profile +No subscription available;No subscription available +Buy;Buy +Redirecting;Redirecting +Apply;Apply +Applying code;Applying code +Invalid subscription code;Invalid subscription code +Cancel Subscription;Cancel Subscription +Active until;Active until +We will send you a notification upon subscription expiration;We will send you a notification upon subscription expiration +This token has been already used;This token has been already used +New login for;New login for +No records found for this day;No records found for this day +Change;Change +Changing;Changing +Minecraft version;Minecraft version +Build version;Build version +Server installation is currently running;Server installation is currently running +Selected;Selected +Move deleted;Move deleted +Delete selected;Delete selected +Log level;Log level +Log message;Log message +Time;Time +Version;Version +You are running moonlight version;You are running moonlight version +Operating system;Operating system +Moonlight is running on;Moonlight is running on +Memory usage;Memory usage +Moonlight is using;Moonlight is using +of memory;of memory +Cpu usage;Cpu usage Refresh;Refresh Send a message to all users;Send a message to all users IP;IP URL;URL Device;Device -Time;Time -Actions;Actions Change url;Change url Message;Message +Enter message;Enter message +Enter the message to send;Enter the message to send +Confirm;Confirm +Are you sure?;Are you sure? Enter url;Enter url -Send;Send -Sending;Sending -Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you -less than a minute ago;less than a minute ago -The support team has been notified. Please be patient;The support team has been notified. Please be patient -is typing;is typing +An unknown error occured while starting backup deletion;An unknown error occured while starting backup deletion +Success;Success +Backup URL successfully copied to your clipboard;Backup URL successfully copied to your clipboard +Backup restore started;Backup restore started +Backup successfully restored;Backup successfully restored +Register for;Register for +Core;Core +Logs;Logs +AuditLog;AuditLog +SecurityLog;SecurityLog +ErrorLog;ErrorLog +Resources;Resources +WinSCP cannot be launched here;WinSCP cannot be launched here +Create a new folder;Create a new folder +Enter a name;Enter a name +File upload complete;File upload complete +New server;New server +Sessions;Sessions +New user;New user +Created at;Created at +Mail template not found;Mail template not found +Missing admin permissions. This attempt has been logged ;) +Address;Address +City;City +State;State +Country;Country +Totp;Totp +Discord;Discord +Subscription;Subscription +None;None +No user with this id found;No user with this id found +Back to list;Back to list +New domain;New domain +Reset password;Reset password +Password reset;Password reset +Reset the password of your account;Reset the password of your account +Wrong here?;Wrong here? +A user with this email can not be found;A user with this email can not be found +Passwort reset successfull. Check your mail;Passwort reset successfull. Check your mail +Discord bot;Discord bot +New image;New image +Description;Description +Uuid;Uuid +Enter tag name;Enter tag name +Remove;Remove +No tags found;No tags found +Enter docker image name;Enter docker image name +Tags;Tags +Docker images;Docker images +Default image;Default image +Startup command;Startup command +Install container;Install container +Install entry;Install entry +Configuration files;Configuration files +Startup detection;Startup detection +Stop command;Stop command +Successfully saved image;Successfully saved image +No docker images found;No docker images found +Key;Key +Default value;Default value +Allocations;Allocations +No variables found;No variables found +Successfully added image;Successfully added image +Password change for;Password change for +of;of +New node;New node +Fqdn;Fqdn +Cores used;Cores used +used;used +5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64 +Host system information;Host system information +0;0 +Docker containers running;Docker containers running +details;details +1;1 +2;2 +DDos;DDos +No ddos attacks found;No ddos attacks found +Node;Node +Date;Date +DDos attack started;DDos attack started +packets;packets +DDos attack stopped;DDos attack stopped + packets; packets +Stop all;Stop all +Kill all;Kill all +Network in;Network in +Network out;Network out +Kill all servers;Kill all servers +Do you really want to kill all running servers?;Do you really want to kill all running servers? +Change power state for;Change power state for +to;to +Stop all servers;Stop all servers +Do you really want to stop all running servers?;Do you really want to stop all running servers? +Manage ;Manage +Manage user ;Manage user +Reloading;Reloading +Update;Update +Updating;Updating +Successfully updated user;Successfully updated user +Discord id;Discord id +Discord username;Discord username +Discord discriminator;Discord discriminator +The Name field is required.;The Name field is required. +An error occured while logging you in;An error occured while logging you in +You need to enter an email address;You need to enter an email address +You need to enter a password;You need to enter a password +You need to enter a password with minimum 8 characters in lenght;You need to enter a password with minimum 8 characters in lenght Proccessing;Proccessing +The FirstName field is required.;The FirstName field is required. +The LastName field is required.;The LastName field is required. +The Address field is required.;The Address field is required. +The City field is required.;The City field is required. +The State field is required.;The State field is required. +The Country field is required.;The Country field is required. +Street and house number requered;Street and house number requered +Max lenght reached;Max lenght reached +Server;Server +stopped;stopped +Cleanups;Cleanups +executed;executed +Used clanup;Used clanup +Enable;Enable +Disabble;Disabble +Disable;Disable +Addons;Addons +Javascript version;Javascript version +Javascript file;Javascript file +Select javascript file to execute on start;Select javascript file to execute on start +Submit;Submit +Processing;Processing +Go up;Go up +Running cleanup;Running cleanup +servers;servers +Select folder to move the file(s) to;Select folder to move the file(s) to +Paper version;Paper version +Join2Start;Join2Start +Server reset;Server reset +Reset;Reset +Resetting;Resetting +Are you sure you want to reset this server?;Are you sure you want to reset this server? +Are you sure? This cannot be undone;Are you sure? This cannot be undone +Resetting server;Resetting server +Deleted file;Deleted file +Reinstalling server;Reinstalling server +Uploading files;Uploading files +complete;complete +Upload complete;Upload complete +Security;Security +Subscriptions;Subscriptions +2fa Code;2fa Code +Your account is secured with 2fa;Your account is secured with 2fa +anyone write a fancy text here?;anyone write a fancy text here? +Activate 2fa;Activate 2fa +2fa apps;2fa apps +Use an app like ;Use an app like +or;or +and scan the following QR Code;and scan the following QR Code +If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;If you have trouble using the QR Code, select manual input in the app and enter your email and the following code: +Finish activation;Finish activation +2fa Code requiered;2fa Code requiered +New password;New password +Secure your account;Secure your account +2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login. +New subscription;New subscription +You need to enter a name;You need to enter a name +You need to enter a description;You need to enter a description +Add new limit;Add new limit +Create subscription;Create subscription +Options;Options +Amount;Amount +Do you really want to delete it?;Do you really want to delete it? +Loading your subscription;Loading your subscription +Searching for deploy node;Searching for deploy node +Searching for available images;Searching for available images +Server details;Server details +Configure your server;Configure your server +Default;Default +You reached the maximum amount of servers for every image of your subscription;You reached the maximum amount of servers for every image of your subscription +Personal information;Personal information +Enter code;Enter code +Server rename;Server rename +Create code;Create code +Save subscription;Save subscription +Enter your information;Enter your information +You need to enter your full name in order to use moonlight;You need to enter your full name in order to use moonlight +No node found;No node found +No node found to deploy to found;No node found to deploy to found +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Server not found;Server not found +A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server +Compress;Compress +Decompress;Decompress +Moving;Moving +Compressing;Compressing +selected;selected +New website;New website +Plesk servers;Plesk servers +Base domain;Base domain +Plesk server;Plesk server +Ftp;Ftp +No SSL certificate found;No SSL certificate found +Ftp Host;Ftp Host +Ftp Port;Ftp Port +Ftp Username;Ftp Username +Ftp Password;Ftp Password +Use;Use +SSL Certificates;SSL Certificates +SSL certificates;SSL certificates +Issue certificate;Issue certificate +New plesk server;New plesk server +Api url;Api url +Host system offline;Host system offline +The host system the website is running on is currently offline;The host system the website is running on is currently offline +No SSL certificates found;No SSL certificates found +No databases found for this website;No databases found for this website +The name should be at least 8 characters long;The name should be at least 8 characters long +The name should only contain of lower case characters and numbers;The name should only contain of lower case characters and numbers +Error from plesk;Error from plesk +Host;Host +Username;Username +SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one +The User field is required.;The User field is required. +You need to specify a owner;You need to specify a owner +You need to specify a image;You need to specify a image +Api Url;Api Url +Api Key;Api Key +Duration;Duration +Enter duration of subscription;Enter duration of subscription +Copied code to clipboard;Copied code to clipboard +Invalid or expired subscription code;Invalid or expired subscription code +Current subscription;Current subscription +You need to specify a server image;You need to specify a server image +CPU;CPU +Hour;Hour +Day;Day +Month;Month +Year;Year +All time;All time +This function is not implemented;This function is not implemented +Domain details;Domain details +Configure your domain;Configure your domain +You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription +You need to specify a shared domain;You need to specify a shared domain +A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain +The Email field is required.;The Email field is required. +The Password field is required.;The Password field is required. +The ConfirmPassword field is required.;The ConfirmPassword field is required. +Passwords need to match;Passwords need to match +Cleanup exception;Cleanup exception +No shared domain found;No shared domain found +Searching for deploy plesk server;Searching for deploy plesk server +No plesk server found;No plesk server found +No plesk server found to deploy to;No plesk server found to deploy to +No node found to deploy to;No node found to deploy to +Website details;Website details +Configure your website;Configure your website +The name cannot be longer that 32 characters;The name cannot be longer that 32 characters +The name should only consist of lower case characters;The name should only consist of lower case characters +News;News +Title...;Title... +Enter text...;Enter text... +Saving...;Saving... +Deleting...;Deleting... +Delete post;Delete post +Do you really want to delete the post ";Do you really want to delete the post " +You have no domains;You have no domains +We were not able to find any domains associated with your account;We were not able to find any domains associated with your account +You have no websites;You have no websites +We were not able to find any websites associated with your account;We were not able to find any websites associated with your account +Guest;Guest +You need a domain;You need a domain +New post;New post +New entry;New entry From ad2be499dba6ea02510aadc64c326dfe4aac4985 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 14 Apr 2023 18:13:51 +0200 Subject: [PATCH 44/45] Some random patches --- Moonlight/App/Helpers/DaemonApiHelper.cs | 6 ++++-- Moonlight/App/Helpers/WingsServerConverter.cs | 13 ++++++++----- Moonlight/App/Services/ServerService.cs | 2 ++ Moonlight/Pages/_Layout.cshtml | 6 +++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Moonlight/App/Helpers/DaemonApiHelper.cs b/Moonlight/App/Helpers/DaemonApiHelper.cs index 4c776a44..b9ffdcce 100644 --- a/Moonlight/App/Helpers/DaemonApiHelper.cs +++ b/Moonlight/App/Helpers/DaemonApiHelper.cs @@ -16,11 +16,13 @@ public class DaemonApiHelper private string GetApiUrl(Node node) { + /* SSL not implemented in moonlight daemon if(node.Ssl) return $"https://{node.Fqdn}:{node.MoonlightDaemonPort}/"; else - return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; - //return $"https://{node.Fqdn}:{node.HttpPort}/"; + return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";*/ + + return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; } public async Task Get(Node node, string resource) diff --git a/Moonlight/App/Helpers/WingsServerConverter.cs b/Moonlight/App/Helpers/WingsServerConverter.cs index 4f22303c..c7375e3d 100644 --- a/Moonlight/App/Helpers/WingsServerConverter.cs +++ b/Moonlight/App/Helpers/WingsServerConverter.cs @@ -118,12 +118,15 @@ public class WingsServerConverter foreach (var section in child.GetSection("find").GetChildren()) { - replaces.Add(new() + if (section.Value != null) { - Match = section.Key, - Replace_With = section.Value - .Replace("{{server.build.default.port}}", def.Port.ToString()) - }); + replaces.Add(new() + { + Match = section.Key, + Replace_With = section.Value + .Replace("{{server.build.default.port}}", def.Port.ToString()) + }); + } } wingsServer.Process_Configuration.Configs.Add(new() diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 01bc2ee0..2e46eb44 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -392,6 +392,8 @@ public class ServerService public async Task Delete(Server s) { + throw new DisplayException("Deleting a server is currently a bit buggy. So its disabled for your safety"); + var server = EnsureNodeData(s); var backups = await GetBackups(server); diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index 58779fee..7a5518e6 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -89,9 +89,9 @@ - - - + + + From 227ad493b734c8d274d56893653f0a7aae58b24c Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:32:50 +0200 Subject: [PATCH 45/45] possible audit log fix --- Moonlight/App/Services/LogServices/AuditLogService.cs | 5 ++++- Moonlight/App/Services/LogServices/ErrorLogService.cs | 5 ++++- Moonlight/App/Services/LogServices/SecurityLogService.cs | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Moonlight/App/Services/LogServices/AuditLogService.cs b/Moonlight/App/Services/LogServices/AuditLogService.cs index 4b0fdf87..a4e31247 100644 --- a/Moonlight/App/Services/LogServices/AuditLogService.cs +++ b/Moonlight/App/Services/LogServices/AuditLogService.cs @@ -73,8 +73,11 @@ public class AuditLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T), diff --git a/Moonlight/App/Services/LogServices/ErrorLogService.cs b/Moonlight/App/Services/LogServices/ErrorLogService.cs index 4328c95a..c53fa112 100644 --- a/Moonlight/App/Services/LogServices/ErrorLogService.cs +++ b/Moonlight/App/Services/LogServices/ErrorLogService.cs @@ -98,8 +98,11 @@ public class ErrorLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T), diff --git a/Moonlight/App/Services/LogServices/SecurityLogService.cs b/Moonlight/App/Services/LogServices/SecurityLogService.cs index f58fa198..ade94fa5 100644 --- a/Moonlight/App/Services/LogServices/SecurityLogService.cs +++ b/Moonlight/App/Services/LogServices/SecurityLogService.cs @@ -72,8 +72,11 @@ public class SecurityLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T),