From 95999eae268e3fa9692c87400d744010e2d4ac88 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Fri, 17 Feb 2023 18:03:52 +0100 Subject: [PATCH] Added new node manager. Added new login/register screen. AuditLog. Permissions --- .gitignore | 10 + Moonlight/App/Database/DataContext.cs | 4 +- .../App/Database/Entities/AuditLogEntry.cs | 12 + Moonlight/App/Database/Entities/Database.cs | 8 + Moonlight/App/Database/Entities/Node.cs | 1 + ...0230217163230_AddNodeSslOption.Designer.cs | 573 ++++++++++++++++++ .../20230217163230_AddNodeSslOption.cs | 29 + .../Migrations/DataContextModelSnapshot.cs | 58 ++ ...30217152643_MigratedSomeModels.Designer.cs | 570 +++++++++++++++++ .../20230217152643_MigratedSomeModels.cs | 70 +++ Moonlight/App/Extensions/StringExtensions.cs | 12 + Moonlight/App/Helpers/Formatter.cs | 37 ++ Moonlight/App/Helpers/StringHelper.cs | 33 + .../Api/Moonlight/AvatarController.cs | 40 ++ .../{Resources.cs => ResourcesController.cs} | 12 +- Moonlight/App/Models/Misc/AuditLogType.cs | 8 + Moonlight/App/Models/Node/CpuStats.cs | 6 + Moonlight/App/Models/Node/DiskStats.cs | 6 + Moonlight/App/Models/Node/DockerStats.cs | 17 + Moonlight/App/Models/Node/MemoryStats.cs | 8 + .../App/Repositories/AuditLogRepository.cs | 21 + .../App/Repositories/DatabaseRepository.cs | 43 ++ Moonlight/App/Services/AuditLogService.cs | 30 + Moonlight/App/Services/NodeService.cs | 13 + .../App/Services/SystemAuditLogService.cs | 25 + Moonlight/App/Services/UserService.cs | 7 +- Moonlight/BlazorApp.razor | 7 +- Moonlight/Moonlight.csproj | 7 +- Moonlight/Program.cs | 5 + .../Components/Alerts/BannedAlert.razor | 2 +- .../Components/Alerts/DisabledAlert.razor | 2 +- Moonlight/Shared/Components/Auth/Login.razor | 161 +++++ .../Shared/Components/Auth/Register.razor | 88 +++ .../Components/Partials/LazyLoader.razor | 50 ++ .../Components/Partials/SidebarMenu.razor | 37 -- .../Components/StateLogic/OnlyAdmin.razor | 36 ++ Moonlight/Shared/Layouts/MainLayout.razor | 75 ++- Moonlight/Shared/Layouts/NotFoundLayout.razor | 134 ++++ Moonlight/Shared/Views/Admin/Nodes/Edit.razor | 115 ++++ .../Shared/Views/Admin/Nodes/Index.razor | 165 +++++ Moonlight/Shared/Views/Admin/Nodes/New.razor | 95 +++ Moonlight/Shared/Views/Index.razor | 233 ++++++- Moonlight/Shared/Views/Setup/Users.razor | 6 + ....GeneratedMSBuildEditorConfig.editorconfig | 32 + .../obj/Debug/net6.0/Moonlight.assets.cache | Bin 28499 -> 28401 bytes .../Moonlight.csproj.AssemblyReference.cache | Bin 201001 -> 201265 bytes .../obj/Moonlight.csproj.nuget.dgspec.json | 8 +- Moonlight/obj/project.assets.json | 73 ++- Moonlight/obj/project.nuget.cache | 18 +- Moonlight/obj/project.packagespec.json | 2 +- Moonlight/resources/lang/de_de.lang | 91 +++ 51 files changed, 2979 insertions(+), 116 deletions(-) create mode 100644 Moonlight/App/Database/Entities/AuditLogEntry.cs create mode 100644 Moonlight/App/Database/Entities/Database.cs create mode 100644 Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.cs create mode 100644 Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.Designer.cs create mode 100644 Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.cs create mode 100644 Moonlight/App/Extensions/StringExtensions.cs create mode 100644 Moonlight/App/Helpers/Formatter.cs create mode 100644 Moonlight/App/Helpers/StringHelper.cs create mode 100644 Moonlight/App/Http/Controllers/Api/Moonlight/AvatarController.cs rename Moonlight/App/Http/Controllers/Api/Moonlight/{Resources.cs => ResourcesController.cs} (52%) create mode 100644 Moonlight/App/Models/Misc/AuditLogType.cs create mode 100644 Moonlight/App/Models/Node/CpuStats.cs create mode 100644 Moonlight/App/Models/Node/DiskStats.cs create mode 100644 Moonlight/App/Models/Node/DockerStats.cs create mode 100644 Moonlight/App/Models/Node/MemoryStats.cs create mode 100644 Moonlight/App/Repositories/AuditLogRepository.cs create mode 100644 Moonlight/App/Repositories/DatabaseRepository.cs create mode 100644 Moonlight/App/Services/AuditLogService.cs create mode 100644 Moonlight/App/Services/NodeService.cs create mode 100644 Moonlight/App/Services/SystemAuditLogService.cs create mode 100644 Moonlight/Shared/Components/Auth/Login.razor create mode 100644 Moonlight/Shared/Components/Auth/Register.razor create mode 100644 Moonlight/Shared/Components/Partials/LazyLoader.razor create mode 100644 Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor create mode 100644 Moonlight/Shared/Layouts/NotFoundLayout.razor create mode 100644 Moonlight/Shared/Views/Admin/Nodes/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Nodes/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Nodes/New.razor diff --git a/.gitignore b/.gitignore index 2ed2d61..ce7c432 100644 --- a/.gitignore +++ b/.gitignore @@ -432,3 +432,13 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml .idea/.idea.Moonlight/.idea/discord.xml +Moonlight/obj/project.nuget.cache +*.editorconfig +*.cache +Moonlight/obj/Debug/net6.0/Moonlight.assets.cache +Moonlight/obj/Debug/net6.0/Moonlight.csproj.AssemblyReference.cache +Moonlight/obj/Debug/net6.0/Moonlight.GeneratedMSBuildEditorConfig.editorconfig +Moonlight/obj/Moonlight.csproj.nuget.dgspec.json +Moonlight/obj/project.assets.json +Moonlight/obj/project.nuget.cache +Moonlight/obj/project.packagespec.json diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index a86c721..9872c22 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -24,7 +24,9 @@ public class DataContext : DbContext public DbSet ServerVariables { get; set; } public DbSet Users { get; set; } public DbSet LoadingMessages { get; set; } - + public DbSet AuditLog { get; set; } + public DbSet Databases { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) diff --git a/Moonlight/App/Database/Entities/AuditLogEntry.cs b/Moonlight/App/Database/Entities/AuditLogEntry.cs new file mode 100644 index 0000000..d600817 --- /dev/null +++ b/Moonlight/App/Database/Entities/AuditLogEntry.cs @@ -0,0 +1,12 @@ +using Moonlight.App.Models.Misc; + +namespace Moonlight.App.Database.Entities; + +public class AuditLogEntry +{ + public int Id { get; set; } + public AuditLogType Type { get; set; } + public string JsonData { get; set; } = ""; + public bool System { get; set; } + public string Ip { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/Database.cs b/Moonlight/App/Database/Entities/Database.cs new file mode 100644 index 0000000..662e0e5 --- /dev/null +++ b/Moonlight/App/Database/Entities/Database.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Database.Entities; + +public class Database +{ + public int Id { get; set; } + public int AaPanelId { get; set; } + public User Owner { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/Node.cs b/Moonlight/App/Database/Entities/Node.cs index 66f12ab..1eb293c 100644 --- a/Moonlight/App/Database/Entities/Node.cs +++ b/Moonlight/App/Database/Entities/Node.cs @@ -11,4 +11,5 @@ public class Node public int HttpPort { get; set; } public int MoonlightDaemonPort { get; set; } public List Allocations { get; set; } = new(); + public bool Ssl { get; set; } = false; } \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.Designer.cs b/Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.Designer.cs new file mode 100644 index 0000000..b6f8463 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.Designer.cs @@ -0,0 +1,573 @@ +// +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("20230217163230_AddNodeSslOption")] + partial class AddNodeSslOption + { + /// + 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.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + 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.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .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("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("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + 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.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.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("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.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("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + 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("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.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + 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.ImageTag", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Tags") + .HasForeignKey("ImageId"); + }); + + 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.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.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Tags"); + + 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/20230217163230_AddNodeSslOption.cs b/Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.cs new file mode 100644 index 0000000..f21b4f7 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230217163230_AddNodeSslOption.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddNodeSslOption : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Ssl", + table: "Nodes", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Ssl", + table: "Nodes"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 06df2d1..2c4e1d2 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -19,6 +19,50 @@ namespace Moonlight.App.Database.Migrations .HasAnnotation("ProductVersion", "7.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("Moonlight.App.Database.Entities.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => { b.Property("Id") @@ -174,6 +218,9 @@ namespace Moonlight.App.Database.Migrations b.Property("SftpPort") .HasColumnType("int"); + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + b.Property("Token") .IsRequired() .HasColumnType("longtext"); @@ -403,6 +450,17 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => { b.HasOne("Moonlight.App.Database.Entities.Image", null) diff --git a/Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.Designer.cs b/Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.Designer.cs new file mode 100644 index 0000000..c3e9640 --- /dev/null +++ b/Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.Designer.cs @@ -0,0 +1,570 @@ +// +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.DatabaseMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230217152643_MigratedSomeModels")] + partial class MigratedSomeModels + { + /// + 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.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + 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.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .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("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("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + 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.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("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.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("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.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("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + 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("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.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + 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.ImageTag", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Tags") + .HasForeignKey("ImageId"); + }); + + 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.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.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Tags"); + + 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/DatabaseMigrations/20230217152643_MigratedSomeModels.cs b/Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.cs new file mode 100644 index 0000000..4c17501 --- /dev/null +++ b/Moonlight/App/DatabaseMigrations/20230217152643_MigratedSomeModels.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.DatabaseMigrations +{ + /// + public partial class MigratedSomeModels : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AuditLog", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Type = table.Column(type: "int", nullable: false), + JsonData = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + System = table.Column(type: "tinyint(1)", nullable: false), + Ip = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AuditLog", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Databases", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + AaPanelId = table.Column(type: "int", nullable: false), + OwnerId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Databases", x => x.Id); + table.ForeignKey( + name: "FK_Databases_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Databases_OwnerId", + table: "Databases", + column: "OwnerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuditLog"); + + migrationBuilder.DropTable( + name: "Databases"); + } + } +} diff --git a/Moonlight/App/Extensions/StringExtensions.cs b/Moonlight/App/Extensions/StringExtensions.cs new file mode 100644 index 0000000..4c48f68 --- /dev/null +++ b/Moonlight/App/Extensions/StringExtensions.cs @@ -0,0 +1,12 @@ +namespace Moonlight.App.Extensions; + +public static class StringExtensions +{ + public static string FirstCharToUpper(this string input) => + input switch + { + null => throw new ArgumentNullException(nameof(input)), + "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)), + _ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1)) + }; +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Formatter.cs b/Moonlight/App/Helpers/Formatter.cs new file mode 100644 index 0000000..d1386f4 --- /dev/null +++ b/Moonlight/App/Helpers/Formatter.cs @@ -0,0 +1,37 @@ +namespace Moonlight.App.Helpers; + +public static class Formatter +{ + public static string FormatUptime(double uptime) + { + TimeSpan t = TimeSpan.FromMilliseconds(uptime); + + return $"{t.Hours}h {t.Minutes}m {t.Seconds}s"; + } + + private static double Round(this double d, int decimals) + { + return Math.Round(d, decimals); + } + + public static string FormatSize(long bytes) + { + var i = Math.Abs(bytes) / 1024D; + if (i < 1) + { + return bytes + " B"; + } + else if (i / 1024D < 1) + { + return i.Round(2) + " KB"; + } + else if (i / (1024D * 1024D) < 1) + { + return (i / 1024D).Round(2) + " MB"; + } + else + { + return (i / (1024D * 1024D)).Round(2) + " GB"; + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/StringHelper.cs b/Moonlight/App/Helpers/StringHelper.cs new file mode 100644 index 0000000..8c322e9 --- /dev/null +++ b/Moonlight/App/Helpers/StringHelper.cs @@ -0,0 +1,33 @@ +using System.Text; + +namespace Moonlight.App.Helpers; + +public static class StringHelper +{ + public static string GenerateString(int length) + { + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var stringBuilder = new StringBuilder(); + var random = new Random(); + + for (int i = 0; i < length; i++) + { + stringBuilder.Append(chars[random.Next(chars.Length)]); + } + + return stringBuilder.ToString(); + } + + public static string IntToStringWithLeadingZeros(int number, int n) + { + string result = number.ToString(); + int length = result.Length; + + for (int i = length; i < n; i++) + { + result = "0" + result; + } + + return result; + } +} \ No newline at end of file diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/AvatarController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/AvatarController.cs new file mode 100644 index 0000000..4a28907 --- /dev/null +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/AvatarController.cs @@ -0,0 +1,40 @@ +using GravatarSharp; +using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Http.Controllers.Api.Moonlight; + +[ApiController] +[Route("api/moonlight/avatar")] +public class AvatarController : Controller +{ + private readonly UserRepository UserRepository; + + public AvatarController(UserRepository userRepository) + { + UserRepository = userRepository; + } + + [HttpGet("{id:int}")] + public async Task GetAvatar([FromRoute] int id) + { + var user = UserRepository.Get().FirstOrDefault(x => x.Id == id); + + if (user == null) + return NotFound(); + + try + { + var url = GravatarController.GetImageUrl(user.Email, 100); + + using var client = new HttpClient(); + var res = await client.GetByteArrayAsync(url); + + return File(res, "image/png"); + } + catch (Exception) + { + return BadRequest(); + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/Resources.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs similarity index 52% rename from Moonlight/App/Http/Controllers/Api/Moonlight/Resources.cs rename to Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs index 2740349..f093785 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/Resources.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs @@ -5,7 +5,7 @@ namespace Moonlight.App.Http.Controllers.Api.Moonlight; [ApiController] [Route("api/moonlight/resources")] -public class Resources : Controller +public class ResourcesController : Controller { [HttpGet("images/{name}")] public ActionResult GetImage([FromRoute] string name) @@ -16,8 +16,14 @@ public class Resources : Controller return NotFound(); } - var fs = new FileStream($"resources/public/images/{name}", FileMode.Open); + if (System.IO.File.Exists($"resources/public/images/{name}")) + { + var fs = new FileStream($"resources/public/images/{name}", FileMode.Open); - return File(fs, "application/octet-stream", name); + 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/Models/Misc/AuditLogType.cs b/Moonlight/App/Models/Misc/AuditLogType.cs new file mode 100644 index 0000000..80ee10d --- /dev/null +++ b/Moonlight/App/Models/Misc/AuditLogType.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Models.Misc; + +public enum AuditLogType +{ + Login, + Register, + LoginFail +} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/CpuStats.cs b/Moonlight/App/Models/Node/CpuStats.cs new file mode 100644 index 0000000..5a12f6e --- /dev/null +++ b/Moonlight/App/Models/Node/CpuStats.cs @@ -0,0 +1,6 @@ +namespace Moonlight.App.Models.Node; + +public class CpuStats +{ + public double CpuUsage { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/DiskStats.cs b/Moonlight/App/Models/Node/DiskStats.cs new file mode 100644 index 0000000..d562f98 --- /dev/null +++ b/Moonlight/App/Models/Node/DiskStats.cs @@ -0,0 +1,6 @@ +namespace Moonlight.App.Models.Node; + +public class DiskStats +{ + public long FreeBytes { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/DockerStats.cs b/Moonlight/App/Models/Node/DockerStats.cs new file mode 100644 index 0000000..791e4e0 --- /dev/null +++ b/Moonlight/App/Models/Node/DockerStats.cs @@ -0,0 +1,17 @@ +namespace Moonlight.App.Models.Node; + +public class DockerStats +{ + public ContainerStats[] Containers { get; set; } + public int NodeId { get; set; } +} + +public class ContainerStats +{ + public Guid Name { get; set; } + public long Memory { get; set; } + public double Cpu { get; set; } + public long NetworkIn { get; set; } + public long NetworkOut { get; set; } + public int NodeId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/MemoryStats.cs b/Moonlight/App/Models/Node/MemoryStats.cs new file mode 100644 index 0000000..335c9fe --- /dev/null +++ b/Moonlight/App/Models/Node/MemoryStats.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Models.Node; + +public class MemoryStats +{ + public long Free { get; set; } + public long Used { get; set; } + public long Total { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/AuditLogRepository.cs b/Moonlight/App/Repositories/AuditLogRepository.cs new file mode 100644 index 0000000..da0bf38 --- /dev/null +++ b/Moonlight/App/Repositories/AuditLogRepository.cs @@ -0,0 +1,21 @@ +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class AuditLogRepository : IDisposable +{ + private readonly DataContext DataContext; + + public AuditLogEntry Add(AuditLogEntry entry) + { + var x = DataContext.AuditLog.Add(entry); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/DatabaseRepository.cs b/Moonlight/App/Repositories/DatabaseRepository.cs new file mode 100644 index 0000000..020c72e --- /dev/null +++ b/Moonlight/App/Repositories/DatabaseRepository.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; + +namespace Moonlight.App.Repositories; + +public class DatabaseRepository : IDisposable +{ + private readonly DataContext DataContext; + + public DatabaseRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.Databases; + } + + public Database.Entities.Database Add(Database.Entities.Database database) + { + var x = DataContext.Databases.Add(database); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(Database.Entities.Database database) + { + DataContext.Databases.Update(database); + DataContext.SaveChanges(); + } + + public void Delete(Database.Entities.Database database) + { + DataContext.Databases.Remove(database); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/AuditLogService.cs b/Moonlight/App/Services/AuditLogService.cs new file mode 100644 index 0000000..ebe83fa --- /dev/null +++ b/Moonlight/App/Services/AuditLogService.cs @@ -0,0 +1,30 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Misc; +using Moonlight.App.Repositories; +using Moonlight.App.Services.Sessions; +using Newtonsoft.Json; + +namespace Moonlight.App.Services; + +public class AuditLogService +{ + private readonly AuditLogRepository AuditLogRepository; + private readonly IdentityService IdentityService; + + public AuditLogService(AuditLogRepository auditLogRepository, IdentityService identityService) + { + AuditLogRepository = auditLogRepository; + IdentityService = identityService; + } + + public void Log(AuditLogType type, object data) + { + AuditLogRepository.Add(new() + { + System = true, + Type = type, + JsonData = JsonConvert.SerializeObject(data), + Ip = IdentityService.GetIp() + }); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/NodeService.cs b/Moonlight/App/Services/NodeService.cs new file mode 100644 index 0000000..fc309a9 --- /dev/null +++ b/Moonlight/App/Services/NodeService.cs @@ -0,0 +1,13 @@ +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services; + +public class NodeService +{ + private readonly NodeRepository NodeRepository; + + public NodeService(NodeRepository nodeRepository) + { + NodeRepository = nodeRepository; + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/SystemAuditLogService.cs b/Moonlight/App/Services/SystemAuditLogService.cs new file mode 100644 index 0000000..1d7b255 --- /dev/null +++ b/Moonlight/App/Services/SystemAuditLogService.cs @@ -0,0 +1,25 @@ +using Moonlight.App.Models.Misc; +using Moonlight.App.Repositories; +using Newtonsoft.Json; + +namespace Moonlight.App.Services; + +public class SystemAuditLogService +{ + private readonly AuditLogRepository AuditLogRepository; + + public SystemAuditLogService(AuditLogRepository auditLogRepository) + { + AuditLogRepository = auditLogRepository; + } + + public void Log(AuditLogType type, object data) + { + AuditLogRepository.Add(new() + { + System = true, + Type = type, + JsonData = JsonConvert.SerializeObject(data) + }); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/UserService.cs b/Moonlight/App/Services/UserService.cs index 07a611f..e0c82d8 100644 --- a/Moonlight/App/Services/UserService.cs +++ b/Moonlight/App/Services/UserService.cs @@ -1,5 +1,6 @@ using JWT.Algorithms; using JWT.Builder; +using Logging.Net; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; using Moonlight.App.Models.Misc; @@ -80,13 +81,13 @@ public class UserService { var user = UserRepository.Get() .FirstOrDefault( - x => x.Email.Equals( - email - ) + x => x.Email == email ); if (user == null) { + Logger.Debug("User not found"); + //AuditLogService.Log("login:fail", $"Invalid email: {email}. Password: {password}"); throw new DisplayException("Email and password combination not found"); } diff --git a/Moonlight/BlazorApp.razor b/Moonlight/BlazorApp.razor index 9cd7516..492dcb0 100644 --- a/Moonlight/BlazorApp.razor +++ b/Moonlight/BlazorApp.razor @@ -2,13 +2,12 @@ - - + Not found - +

Sorry, there's nothing at this address.

-
+ \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index dbaf2c7..5bde6d3 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -17,7 +17,7 @@ - + @@ -25,6 +25,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -56,6 +60,7 @@ + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index bf288ee..9ec7b69 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -33,6 +33,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); // Services builder.Services.AddSingleton(); @@ -46,6 +48,9 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + // Helpers builder.Services.AddSingleton(); diff --git a/Moonlight/Shared/Components/Alerts/BannedAlert.razor b/Moonlight/Shared/Components/Alerts/BannedAlert.razor index cf1e10f..264d4bb 100644 --- a/Moonlight/Shared/Components/Alerts/BannedAlert.razor +++ b/Moonlight/Shared/Components/Alerts/BannedAlert.razor @@ -10,7 +10,7 @@
- Logo + Logo

Your account is banned from moonlight

diff --git a/Moonlight/Shared/Components/Alerts/DisabledAlert.razor b/Moonlight/Shared/Components/Alerts/DisabledAlert.razor index dd5746c..f0d095b 100644 --- a/Moonlight/Shared/Components/Alerts/DisabledAlert.razor +++ b/Moonlight/Shared/Components/Alerts/DisabledAlert.razor @@ -10,7 +10,7 @@
- Logo + Logo

Your moonlight account is disabled

diff --git a/Moonlight/Shared/Components/Auth/Login.razor b/Moonlight/Shared/Components/Auth/Login.razor new file mode 100644 index 0000000..5dfe2fb --- /dev/null +++ b/Moonlight/Shared/Components/Auth/Login.razor @@ -0,0 +1,161 @@ +@page "/login" + +@* This is just a "virtual" route/page. The handling for that is +@* MainLayout doing for us. We need to put that here so the router +@* does not return the 404 page +*@ + +@using Moonlight.App.Services.Interop +@using Moonlight.App.Services +@using Moonlight.App.Exceptions +@using Logging.Net +@using Moonlight.App.Services.Sessions + +@inject AlertService AlertService +@inject UserService UserService +@inject SmartTranslateService SmartTranslateService +@inject CookieService CookieService +@inject NavigationManager NavigationManager + +
+
+
+
+
+ @if (!TotpRequired) + { +
+

+ Sign In +

+
+ Sign in to start with moonlight +
+
+ + + +
+ + Or with email + +
+ +
+ +
+ +
+ +
+ + + +
+ + +
+ } + else + { +
+ +
+
+ + +
+ } + +
+ Not registered yet? + + + Sign up + +
+
+
+
+
+
+ +@code +{ + private string Email = ""; + private string Password = ""; + + private bool TotpRequired = false; + private string TotpCode = ""; + + private async Task DoLogin() + { + try + { + Email = Email.ToLower().Trim(); + + TotpRequired = await UserService.CheckTotp(Email, Password); + + if (!TotpRequired) + { + var token = await UserService.Login(Email, Password); + await CookieService.SetValue("token", token, 10); + + if(NavigationManager.Uri.EndsWith("login")) + NavigationManager.NavigateTo("/", true); + else + NavigationManager.NavigateTo(NavigationManager.Uri, true); + } + else + { + await InvokeAsync(StateHasChanged); + } + } + catch (DisplayException e) + { + await AlertService.Error( + SmartTranslateService.Translate("Error"), + SmartTranslateService.Translate(e.Message) + ); + } + catch (Exception e) + { + await AlertService.Error( + SmartTranslateService.Translate("Error"), + SmartTranslateService.Translate("An error occured while logging you in") + ); + + Logger.Error("Error while login"); + Logger.Error(e); + } + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Auth/Register.razor b/Moonlight/Shared/Components/Auth/Register.razor new file mode 100644 index 0000000..e3fbf61 --- /dev/null +++ b/Moonlight/Shared/Components/Auth/Register.razor @@ -0,0 +1,88 @@ +@page "/register" + +@* This is just a "virtual" route/page. The handling for that is +@* MainLayout doing for us. We need to put that here so the router +@* does not return the 404 page +*@ + +@using Moonlight.App.Services + +@inject SmartTranslateService SmartTranslateService + +
+
+
+
+
+
+

+ Sign Up +

+
+ Sign up to start with moonlight +
+
+ + + +
+ + Or with email + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ Already registered? + + + Sign in + +
+
+
+
+
+
\ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/LazyLoader.razor b/Moonlight/Shared/Components/Partials/LazyLoader.razor new file mode 100644 index 0000000..63cc8a8 --- /dev/null +++ b/Moonlight/Shared/Components/Partials/LazyLoader.razor @@ -0,0 +1,50 @@ +@if (loaded) +{ + @ChildContent +} +else +{ +
+ + @(Text) +
+} + +@code +{ + [Parameter] + public RenderFragment ChildContent { get; set; } + + [Parameter] + public Func Load { get; set; } + + [Parameter] + public string Text { get; set; } = ""; + + private bool loaded = false; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await Load.Invoke(this); + loaded = true; + await InvokeAsync(StateHasChanged); + } + } + + public async Task SetText(string text) + { + Text = text; + await InvokeAsync(StateHasChanged); + } + + public async Task Reload() + { + loaded = false; + await InvokeAsync(StateHasChanged); + await Load.Invoke(this); + loaded = true; + await InvokeAsync(StateHasChanged); + } +} diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index d8cfb09..2c38758 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -36,43 +36,6 @@ else Dashboard
-