Browse Source

Merge branch 'main' into NewTypeahead

Marcel Baumgartner 2 năm trước cách đây
mục cha
commit
fe4eb95247

+ 1 - 0
Moonlight/App/Database/DataContext.cs

@@ -41,6 +41,7 @@ public class DataContext : DbContext
     public DbSet<Subscription> Subscriptions { get; set; }
     public DbSet<PleskServer> PleskServers { get; set; }
     public DbSet<Website> Websites { get; set; }
+    public DbSet<StatisticsData> Statistics { get; set; }
 
     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     {

+ 12 - 0
Moonlight/App/Database/Entities/StatisticsData.cs

@@ -0,0 +1,12 @@
+namespace Moonlight.App.Database.Entities;
+
+public class StatisticsData
+{
+    public int Id { get; set; }
+    
+    public string Chart { get; set; }
+    
+    public double Value { get; set; }
+    
+    public DateTime Date { get; set; }
+}

+ 1 - 1
Moonlight/App/Database/Entities/User.cs

@@ -37,7 +37,7 @@ public class User
     public DateTime TokenValidTime { get; set; } = DateTime.Now;
     
     // Discord
-    public long DiscordId { get; set; } = -1;
+    public ulong DiscordId { get; set; }
     
     // Date stuff
     public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

+ 894 - 0
Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs

@@ -0,0 +1,894 @@
+// <auto-generated />
+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
+    {
+        /// <inheritdoc />
+        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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<long>("Data")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Ongoing")
+                        .HasColumnType("tinyint(1)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("NodeId");
+
+                    b.ToTable("DdosAttacks");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Default")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int?>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ImageId");
+
+                    b.ToTable("DockerImages");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("Allocations")
+                        .HasColumnType("int");
+
+                    b.Property<string>("ConfigFiles")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Description")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallDockerImage")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallEntrypoint")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallScript")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Startup")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("StartupDetection")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("StopCommand")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("TagsJson")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<Guid>("Uuid")
+                        .HasColumnType("char(36)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Images");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ImageTags");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("DefaultValue")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Key")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ImageId");
+
+                    b.ToTable("ImageVariables");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Message")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("LoadingMessages");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("AuditLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Class")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Stacktrace")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ErrorLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SecurityLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Fqdn")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("HttpPort")
+                        .HasColumnType("int");
+
+                    b.Property<int>("MoonlightDaemonPort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("SftpPort")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Ssl")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("Token")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("TokenId")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Nodes");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int?>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Port")
+                        .HasColumnType("int");
+
+                    b.Property<int?>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Action")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NotificationClientId")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("NotificationClientId");
+
+                    b.ToTable("NotificationActions");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("NotificationClients");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Identifier")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Revokes");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("Cpu")
+                        .HasColumnType("int");
+
+                    b.Property<long>("Disk")
+                        .HasColumnType("bigint");
+
+                    b.Property<int>("DockerImageIndex")
+                        .HasColumnType("int");
+
+                    b.Property<int>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Installing")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsCleanupException")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("MainAllocationId")
+                        .HasColumnType("int");
+
+                    b.Property<long>("Memory")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("OverrideStartup")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Suspended")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<Guid>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<long>("Bytes")
+                        .HasColumnType("bigint");
+
+                    b.Property<bool>("Created")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ServerId")
+                        .HasColumnType("int");
+
+                    b.Property<Guid>("Uuid")
+                        .HasColumnType("char(36)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ServerId");
+
+                    b.ToTable("ServerBackups");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Key")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ServerId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Value")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ServerId");
+
+                    b.ToTable("ServerVariables");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("CloudflareId")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SharedDomains");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Chart")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<double>("Value")
+                        .HasColumnType("double");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Statistics");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Description")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("LimitsJson")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Subscriptions");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Answer")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("IsQuestion")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsSupport")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsSystem")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("Message")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("RecipientId")
+                        .HasColumnType("int");
+
+                    b.Property<int?>("SenderId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Address")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("Admin")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("City")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Country")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<int?>("CurrentSubscriptionId")
+                        .HasColumnType("int");
+
+                    b.Property<long>("DiscordId")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("FirstName")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("LastName")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("State")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("Status")
+                        .HasColumnType("int");
+
+                    b.Property<int>("SubscriptionDuration")
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("SubscriptionSince")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("SupportPending")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<DateTime>("TokenValidTime")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("TotpEnabled")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("TotpSecret")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("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
+        }
+    }
+}

+ 38 - 0
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
+{
+    /// <inheritdoc />
+    public partial class AddedStatistics : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "Statistics",
+                columns: table => new
+                {
+                    Id = table.Column<int>(type: "int", nullable: false)
+                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+                    Chart = table.Column<string>(type: "longtext", nullable: false)
+                        .Annotation("MySql:CharSet", "utf8mb4"),
+                    Value = table.Column<double>(type: "double", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_Statistics", x => x.Id);
+                })
+                .Annotation("MySql:CharSet", "utf8mb4");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "Statistics");
+        }
+    }
+}

+ 975 - 0
Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs

@@ -0,0 +1,975 @@
+// <auto-generated />
+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
+    {
+        /// <inheritdoc />
+        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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<long>("Data")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Ongoing")
+                        .HasColumnType("tinyint(1)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("NodeId");
+
+                    b.ToTable("DdosAttacks");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Default")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int?>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ImageId");
+
+                    b.ToTable("DockerImages");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("Allocations")
+                        .HasColumnType("int");
+
+                    b.Property<string>("ConfigFiles")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Description")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallDockerImage")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallEntrypoint")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("InstallScript")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Startup")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("StartupDetection")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("StopCommand")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("TagsJson")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<Guid>("Uuid")
+                        .HasColumnType("char(36)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Images");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ImageTags");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("DefaultValue")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Key")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ImageId");
+
+                    b.ToTable("ImageVariables");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Message")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("LoadingMessages");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("AuditLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Class")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Stacktrace")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ErrorLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Ip")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("JsonData")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("System")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SecurityLog");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Fqdn")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("HttpPort")
+                        .HasColumnType("int");
+
+                    b.Property<int>("MoonlightDaemonPort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("SftpPort")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Ssl")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("Token")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("TokenId")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Nodes");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int?>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Port")
+                        .HasColumnType("int");
+
+                    b.Property<int?>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Action")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NotificationClientId")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("NotificationClientId");
+
+                    b.ToTable("NotificationActions");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("UserId")
+                        .HasColumnType("int");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserId");
+
+                    b.ToTable("NotificationClients");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("ApiKey")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("ApiUrl")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("PleskServers");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Identifier")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Revokes");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<int>("Cpu")
+                        .HasColumnType("int");
+
+                    b.Property<long>("Disk")
+                        .HasColumnType("bigint");
+
+                    b.Property<int>("DockerImageIndex")
+                        .HasColumnType("int");
+
+                    b.Property<int>("ImageId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Installing")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsCleanupException")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<int>("MainAllocationId")
+                        .HasColumnType("int");
+
+                    b.Property<long>("Memory")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("NodeId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("OverrideStartup")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("Suspended")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<Guid>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<long>("Bytes")
+                        .HasColumnType("bigint");
+
+                    b.Property<bool>("Created")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ServerId")
+                        .HasColumnType("int");
+
+                    b.Property<Guid>("Uuid")
+                        .HasColumnType("char(36)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ServerId");
+
+                    b.ToTable("ServerBackups");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Key")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("ServerId")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Value")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ServerId");
+
+                    b.ToTable("ServerVariables");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("CloudflareId")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SharedDomains");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Chart")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<double>("Value")
+                        .HasColumnType("double");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Statistics");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Description")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("LimitsJson")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Subscriptions");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Answer")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("IsQuestion")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsSupport")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<bool>("IsSystem")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("Message")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int?>("RecipientId")
+                        .HasColumnType("int");
+
+                    b.Property<int?>("SenderId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("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<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Address")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<bool>("Admin")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("City")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Country")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("CreatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<int?>("CurrentSubscriptionId")
+                        .HasColumnType("int");
+
+                    b.Property<long>("DiscordId")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("FirstName")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("LastName")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("State")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("Status")
+                        .HasColumnType("int");
+
+                    b.Property<int>("SubscriptionDuration")
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("SubscriptionSince")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("SupportPending")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<DateTime>("TokenValidTime")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<bool>("TotpEnabled")
+                        .HasColumnType("tinyint(1)");
+
+                    b.Property<string>("TotpSecret")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("UpdatedAt")
+                        .HasColumnType("datetime(6)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CurrentSubscriptionId");
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("BaseDomain")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("FtpLogin")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("FtpPassword")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("PleskId")
+                        .HasColumnType("int");
+
+                    b.Property<int>("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
+        }
+    }
+}

+ 30 - 0
Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs

@@ -0,0 +1,30 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Moonlight.App.Database.Migrations
+{
+    /// <inheritdoc />
+    public partial class ChangedStatisticsModel : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.AddColumn<DateTime>(
+                name: "Date",
+                table: "Statistics",
+                type: "datetime(6)",
+                nullable: false,
+                defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropColumn(
+                name: "Date",
+                table: "Statistics");
+        }
+    }
+}

+ 21 - 0
Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs

@@ -571,6 +571,27 @@ namespace Moonlight.App.Database.Migrations
                     b.ToTable("SharedDomains");
                 });
 
+            modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<string>("Chart")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<double>("Value")
+                        .HasColumnType("double");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Statistics");
+                });
+
             modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b =>
                 {
                     b.Property<int>("Id")

+ 143 - 0
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<FileData[]> Ls()
+    {
+        var x = new List<FileData>();
+        
+        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<string> 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<int>? 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<string> Pwd()
+    {
+        return Task.FromResult(CurrentPath);
+    }
+
+    public override Task<string> DownloadUrl(FileData fileData)
+    {
+        throw new NotImplementedException();
+    }
+
+    public override async Task<Stream> 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<string> GetLaunchUrl()
+    {
+        throw new NotImplementedException();
+    }
+
+    public override object Clone()
+    {
+        return new HostFileAccess(BasePath);
+    }
+}

+ 10 - 0
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
+}

+ 37 - 0
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<StatisticsData> 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, Date = DateTime.Now});
+    }
+
+    public void Dispose()
+    {
+        DataContext.Dispose();
+    }
+}

+ 1 - 1
Moonlight/App/Services/OAuth2/DiscordOAuth2Service.cs

@@ -115,7 +115,7 @@ public class DiscordOAuth2Service
             Email = getData.GetValue<string>("email"),
             FirstName = "User",
             LastName = "User",
-            DiscordId = getData.GetValue<long>("id"),
+            DiscordId = getData.GetValue<ulong>("id"),
             Status = UserStatus.DataPending
         };
     }

+ 57 - 0
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<DataContext>();
+        ConfigService = configService;
+        StatisticsRepository = provider.GetRequiredService<StatisticsRepository>();
+        WebsiteService = provider.GetRequiredService<WebsiteService>();
+        PleskServerRepository = provider.GetRequiredService<PleskServerRepository>();
+
+        var config = ConfigService.GetSection("Moonlight").GetSection("Statistics");
+        if(!config.GetValue<bool>("Enabled"))
+            return;
+
+        var _period = config.GetValue<int>("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);
+        }
+    }
+}

+ 24 - 0
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();
+    }
+}

+ 1 - 1
Moonlight/App/Services/UserService.cs

@@ -69,7 +69,7 @@ public class UserService
             State = "",
             Status = UserStatus.Unverified,
             CreatedAt = DateTime.UtcNow,
-            DiscordId = -1,
+            DiscordId = 0,
             TotpEnabled = false,
             TotpSecret = "",
             UpdatedAt = DateTime.UtcNow,

+ 1 - 0
Moonlight/Moonlight.csproj

@@ -9,6 +9,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Blazor-ApexCharts" Version="0.9.16-beta" />
     <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
     <PackageReference Include="Ben.Demystifier" Version="0.4.1" />
     <PackageReference Include="Blazor.ContextMenu" Version="1.15.0" />

+ 2 - 0
Moonlight/Pages/_Layout.cshtml

@@ -112,5 +112,7 @@
 <script src="/assets/js/snow.js"></script>
 <script src="/assets/js/recaptcha.js"></script>
 <script src="/assets/js/moonlight.js"></script>
+<script src="_content/Blazor-ApexCharts/js/apex-charts.min.js"></script>
+<script src="_content/Blazor-ApexCharts/js/blazor-apex-charts.js"></script>
 </body>
 </html>

+ 6 - 0
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<WebsiteRepository>();
             builder.Services.AddScoped<LoadingMessageRepository>();
 
+            builder.Services.AddScoped<StatisticsRepository>();
+            
             builder.Services.AddScoped<AuditLogEntryRepository>();
             builder.Services.AddScoped<ErrorLogEntryRepository>();
             builder.Services.AddScoped<SecurityLogEntryRepository>();
@@ -93,6 +96,7 @@ namespace Moonlight
             builder.Services.AddScoped<ModalService>();
             builder.Services.AddScoped<SmartDeployService>();
             builder.Services.AddScoped<WebsiteService>();
+            builder.Services.AddScoped<StatisticsViewService>();
             
             builder.Services.AddScoped<GoogleOAuth2Service>();
             builder.Services.AddScoped<DiscordOAuth2Service>();
@@ -128,6 +132,7 @@ namespace Moonlight
             
             // Background services
             builder.Services.AddSingleton<DiscordBotService>();
+            builder.Services.AddSingleton<StatisticsCaptureService>();
 
             // Third party services
             builder.Services.AddBlazorTable();
@@ -160,6 +165,7 @@ namespace Moonlight
             // AutoStart services
             _ = app.Services.GetRequiredService<CleanupService>();
             _ = app.Services.GetRequiredService<DiscordBotService>();
+            _ = app.Services.GetRequiredService<StatisticsCaptureService>();
             
             // Discord bot service
             //var discordBotService = app.Services.GetRequiredService<DiscordBotService>();

+ 128 - 0
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
+
+<OnlyAdmin>
+    <div class="row mt-4 mb-2">
+        <div class="col-12 col-lg-6 col-xl">
+            <div class="card card-body">
+                <select class="form-select" @bind="bind">
+                    <option value="1"><TL>Hour</TL></option>
+                    <option value="24"><TL>Day</TL></option>
+                    <option value="744"><TL>Month</TL></option>
+                    <option value="8760"><TL>Year</TL></option>
+                    <option value="867240"><TL>All time</TL></option>
+                </select>
+            </div>
+        </div>
+    </div>
+    
+    <LazyLoader @ref="Loader" Load="Load">
+            @foreach (var charts in Charts.Chunk(2))
+            {
+                <div class="row">
+                    @foreach (var chart in charts)
+                    {
+                        <div class="col-sm-6">
+                            <div class="card mt-4">
+                                <div class="card-header">
+                                    <div class="card-title">
+                                        <TL>@chart.Header</TL>
+                                    </div>
+                                </div>
+                                <div class="card-body">
+                                    <ApexChart TItem="StatisticsData"
+                                               Options="GenerateOptions()"
+                                               OnRendered="OnChartRendered">
+                                        <ApexPointSeries TItem="StatisticsData"
+                                                         Items="chart.Data"
+                                                         SeriesType="SeriesType.Area"
+                                                         Name=""
+                                                         ShowDataLabels="false"
+                                                         XValue="@(e => FormatDate(e.Date))"
+                                                         YValue="@(e => (decimal) Math.Round(e.Value))"/>
+                                    </ApexChart>
+                                </div>
+                            </div>
+                        </div>
+                    }
+                </div>
+            }
+    </LazyLoader>
+</OnlyAdmin>
+
+@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<StatisticsData> 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()
+    {
+    }
+}

+ 3 - 3
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
 
 <OnlyAdmin>
     <AdminSystemNavigation Index="5" />
     
     <div class="card card-body">
-        <FileManager FileAccess="@(new HostFileAccess("resources"))">
+        <FileManager Access="@(new HostFileAccess("resources"))">
         </FileManager>        
     </div>
 </OnlyAdmin>

+ 6 - 0
Moonlight/resources/lang/de_de.lang

@@ -525,3 +525,9 @@ 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