Quellcode durchsuchen

Merge pull request #61 from Moonlight-Panel/NewsPage

News page
Daniel Balk vor 2 Jahren
Ursprung
Commit
631f54dd52

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

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

+ 10 - 0
Moonlight/App/Database/Entities/NewsEntry.cs

@@ -0,0 +1,10 @@
+namespace Moonlight.App.Database.Entities;
+
+public class NewsEntry
+{
+    public int Id { get; set; }
+    
+    public DateTime Date { get; set; }
+    public string Title { get; set; }
+    public string Markdown { get; set; }
+}

+ 997 - 0
Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs

@@ -0,0 +1,997 @@
+// <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("20230412162710_NewsEntriesTableAdded")]
+    partial class NewsEntriesTableAdded
+    {
+        /// <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.NewsEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Markdown")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("NewsEntries");
+                });
+
+            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<ulong>("DiscordId")
+                        .HasColumnType("bigint unsigned");
+
+                    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
+        }
+    }
+}

+ 57 - 0
Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs

@@ -0,0 +1,57 @@
+using System;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Moonlight.App.Database.Migrations
+{
+    /// <inheritdoc />
+    public partial class NewsEntriesTableAdded : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.AlterColumn<ulong>(
+                name: "DiscordId",
+                table: "Users",
+                type: "bigint unsigned",
+                nullable: false,
+                oldClrType: typeof(long),
+                oldType: "bigint");
+
+            migrationBuilder.CreateTable(
+                name: "NewsEntries",
+                columns: table => new
+                {
+                    Id = table.Column<int>(type: "int", nullable: false)
+                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
+                    Date = table.Column<DateTime>(type: "datetime(6)", nullable: false),
+                    Title = table.Column<string>(type: "longtext", nullable: false)
+                        .Annotation("MySql:CharSet", "utf8mb4"),
+                    Markdown = table.Column<string>(type: "longtext", nullable: false)
+                        .Annotation("MySql:CharSet", "utf8mb4")
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_NewsEntries", x => x.Id);
+                })
+                .Annotation("MySql:CharSet", "utf8mb4");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "NewsEntries");
+
+            migrationBuilder.AlterColumn<long>(
+                name: "DiscordId",
+                table: "Users",
+                type: "bigint",
+                nullable: false,
+                oldClrType: typeof(ulong),
+                oldType: "bigint unsigned");
+        }
+    }
+}

+ 24 - 2
Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs

@@ -296,6 +296,28 @@ namespace Moonlight.App.Database.Migrations
                     b.ToTable("SecurityLog");
                 });
 
+            modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("int");
+
+                    b.Property<DateTime>("Date")
+                        .HasColumnType("datetime(6)");
+
+                    b.Property<string>("Markdown")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasColumnType("longtext");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("NewsEntries");
+                });
+
             modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b =>
                 {
                     b.Property<int>("Id")
@@ -686,8 +708,8 @@ namespace Moonlight.App.Database.Migrations
                     b.Property<int?>("CurrentSubscriptionId")
                         .HasColumnType("int");
 
-                    b.Property<long>("DiscordId")
-                        .HasColumnType("bigint");
+                    b.Property<ulong>("DiscordId")
+                        .HasColumnType("bigint unsigned");
 
                     b.Property<string>("Email")
                         .IsRequired()

+ 44 - 0
Moonlight/App/Repositories/NewsEntryRepository.cs

@@ -0,0 +1,44 @@
+using Microsoft.EntityFrameworkCore;
+using Moonlight.App.Database;
+using Moonlight.App.Database.Entities;
+
+namespace Moonlight.App.Repositories;
+
+public class NewsEntryRepository
+{
+    private readonly DataContext DataContext;
+
+    public NewsEntryRepository(DataContext dataContext)
+    {
+        DataContext = dataContext;
+    }
+
+    public DbSet<NewsEntry> Get()
+    {
+        return DataContext.NewsEntries;
+    }
+
+    public NewsEntry Add(NewsEntry newsEntry)
+    {
+        var x = DataContext.NewsEntries.Add(newsEntry);
+        DataContext.SaveChanges();
+        return x.Entity;
+    }
+
+    public void Update(NewsEntry newsEntry)
+    {
+        DataContext.NewsEntries.Update(newsEntry);
+        DataContext.SaveChanges();
+    }
+
+    public void Delete(NewsEntry newsEntry)
+    {
+        DataContext.NewsEntries.Remove(newsEntry);
+        DataContext.SaveChanges();
+    }
+    
+    public void Dispose()
+    {
+        DataContext.Dispose();
+    }
+}

+ 1 - 0
Moonlight/Moonlight.csproj

@@ -24,6 +24,7 @@
     <PackageReference Include="JWT" Version="10.0.2" />
     <PackageReference Include="Logging.Net" Version="1.1.0" />
     <PackageReference Include="Mappy.Net" Version="1.0.2" />
+    <PackageReference Include="Markdig" Version="0.31.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

+ 1 - 0
Moonlight/Program.cs

@@ -64,6 +64,7 @@ namespace Moonlight
             builder.Services.AddScoped<PleskServerRepository>();
             builder.Services.AddScoped<WebsiteRepository>();
             builder.Services.AddScoped<LoadingMessageRepository>();
+            builder.Services.AddScoped<NewsEntryRepository>();
 
             builder.Services.AddScoped<StatisticsRepository>();
             

+ 98 - 0
Moonlight/Shared/Components/News/NewsEditor.razor

@@ -0,0 +1,98 @@
+@using Moonlight.App.Services
+@using Moonlight.App.Database.Entities
+@using BlazorMonaco
+
+@inject SmartTranslateService SmartTranslateService
+@inject IJSRuntime JsRuntime
+
+<div class="card mb-6">
+    <div class="card-header">
+        <h3 class="card-title w-75">
+            <input type="text" @bind="Model.Title" placeholder="@SmartTranslateService.Translate("Title...")" class="form-control form-control-flush"/>
+        </h3>
+        <div class="card-toolbar">
+            @{
+                string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString();
+                var date = Model.Date == default ? DateTime.Now : Model.Date;
+            }
+            <span class="text-gray-600 fw-semibold">@dateInt(date.Day).@dateInt(date.Month).@date.Year</span>
+        </div>
+    </div>
+    <div class="card-body">
+        <MonacoEditor CssClass="h-50" @ref="Editor" Id="vseditor" ConstructionOptions="(x) => EditorOptions"/>
+    </div>
+    <div class="card-footer">
+        <WButton CssClasses="btn btn-primary float-end" OnClick="DoSave" Text="@SmartTranslateService.Translate("Save")" WorkingText="@SmartTranslateService.Translate("Saving...")"></WButton>
+    </div>
+</div>
+
+@code {
+    // Monaco Editor
+    private MonacoEditor Editor;
+    private StandaloneEditorConstructionOptions EditorOptions;
+    
+    [Parameter]
+    public NewsEntry Model { get; set; }
+
+    [Parameter]
+    public Func<NewsEntry, Task> Save { get; set; }
+
+    protected override void OnInitialized()
+    {
+        EditorOptions = new()
+        {
+            AutomaticLayout = true,
+            Language = "plaintext",
+            Value = "Loading content",
+            Theme = "moonlight-theme",
+            Contextmenu = false,
+            Minimap = new()
+            {
+                Enabled = false
+            },
+            AutoIndent = true
+        };
+    }
+    
+    protected override async Task OnAfterRenderAsync(bool firstRender)
+    {
+        if (firstRender)
+        {
+            await JsRuntime.InvokeVoidAsync("initMonacoTheme");
+            
+            Editor.OnDidInit = new EventCallback<MonacoEditorBase>(this, async () =>
+            {
+                EditorOptions.Language = "markdown";
+        
+                var model = await Editor.GetModel();
+                await MonacoEditorBase.SetModelLanguage(model, EditorOptions.Language);
+                await Editor.SetPosition(new Position()
+                {
+                    Column = 0,
+                    LineNumber = 1
+                });
+        
+                await Editor.SetValue(string.IsNullOrWhiteSpace(Model.Markdown) ? "*enter your markdown here*" : Model.Markdown);
+        
+                await Editor.Layout(new Dimension()
+                {
+                    Height = 500,
+                    Width = 1000
+                }); 
+            });
+        }
+    }
+
+    private async Task DoSave()
+    {
+        Model.Date = Model.Date == default ? DateTime.Now : Model.Date;
+        Model.Markdown = await Editor.GetValue();
+        
+        Save?.Invoke(Model);
+    }
+
+    public async Task UpdateMonacoText()
+    {
+        await Editor.SetValue(Model.Markdown);
+    }
+}

+ 8 - 0
Moonlight/Shared/Components/Partials/SidebarMenu.razor

@@ -68,6 +68,14 @@ else
             <span class="menu-title"><TL>Changelog</TL></span>
         </a>
     </div>
+    <div class="menu-item">
+        <a class="menu-link" href="/news">
+            <span class="menu-icon">
+                <i class="bx bx-news"></i>
+            </span>
+            <span class="menu-title"><TL>News</TL></span>
+        </a>
+    </div>
 
     if (User.Admin)
     {

+ 4 - 1
Moonlight/Shared/Components/StateLogic/OnlyAdmin.razor

@@ -7,7 +7,7 @@
     {
         @ChildContent
     }
-    else
+    else if(!Silent)
     {
         <div class="alert alert-danger">
             <TL>Missing admin permissions. This attempt has been logged ;)</TL>
@@ -22,4 +22,7 @@
 
     [CascadingParameter]
     public User? User { get; set; }
+
+    [Parameter]
+    public bool Silent { get; set; } = false;
 }

+ 32 - 0
Moonlight/Shared/News/Edit.razor

@@ -0,0 +1,32 @@
+@page "/news/edit/{Id:int}"
+
+@using Moonlight.App.Database.Entities
+@using Moonlight.App.Repositories
+@using Moonlight.Shared.Components.News
+
+@inject NewsEntryRepository NewsEntryRepository
+@inject NavigationManager NavigationManager
+
+<OnlyAdmin>
+    <LazyLoader Load="Load">
+        <NewsEditor Model="Entry" Save="DoSave"></NewsEditor>
+    </LazyLoader>
+</OnlyAdmin>
+
+@code {
+    [Parameter]
+    public int Id { get; set; }
+
+    private NewsEntry Entry;
+
+    private async Task Load(LazyLoader loader)
+    {
+        Entry = NewsEntryRepository.Get().First(x => x.Id == Id);
+    }
+
+    private async Task DoSave(NewsEntry entry)
+    {
+        NewsEntryRepository.Update(entry);
+        NavigationManager.NavigateTo("/news");
+    }
+}

+ 89 - 0
Moonlight/Shared/Views/News.razor

@@ -0,0 +1,89 @@
+@page "/news"
+@using Moonlight.App.Repositories
+@using Moonlight.App.Database.Entities
+@using Markdig
+@using Moonlight.App.Services
+@using Moonlight.App.Services.Interop
+@using Moonlight.Shared.Components.News
+
+@inject NewsEntryRepository NewsEntryRepository
+@inject SmartTranslateService SmartTranslateService
+@inject NavigationManager NavigationManager
+@inject AlertService AlertService
+
+<OnlyAdmin Silent="true">
+    <NewsEditor @ref="NewPostEditor" Model="NewPost" Save="DoSaveNewPost"></NewsEditor>
+</OnlyAdmin>
+
+<LazyLoader Load="Load">
+    @foreach (var entry in Entries)
+    {
+        <div class="card mb-6">
+            <div class="card-header">
+                <h3 class="card-title">@entry.Title</h3>
+                <div class="card-toolbar">
+                    <OnlyAdmin>
+                        <a href="/news/edit/@entry.Id">
+                            <button class="btn btn-sm btn-light me-4">
+                                <TL>Edit</TL>
+                            </button>
+                        </a>
+                        
+                        <WButton CssClasses="btn btn-sm btn-light me-4" 
+                                 Text="@SmartTranslateService.Translate("Delete")"
+                                 WorkingText="@SmartTranslateService.Translate("Deleting...")"
+                                 OnClick="() => Delete(entry)"></WButton>
+                    </OnlyAdmin>
+                    
+                    @{
+                        string dateInt(int i) => i.ToString().Length < 2 ? "0" + i : i.ToString();
+                    }
+                    <span class="text-gray-600 fw-semibold">@dateInt(entry.Date.Day).@dateInt(entry.Date.Month).@entry.Date.Year</span>
+                </div>
+            </div>
+            <div class="card-body">
+                @{
+                    var html = (MarkupString)Markdown.ToHtml(entry.Markdown);
+                }
+                
+                @html
+            </div>
+        </div>
+
+    }
+</LazyLoader>
+
+@code {
+    private NewsEntry NewPost = new();
+    private NewsEditor NewPostEditor;
+    
+    private NewsEntry[] Entries;
+
+    private async Task Load(LazyLoader loader)
+    {
+        Entries = NewsEntryRepository.Get().OrderByDescending(x => x.Date).ToArray();
+    }
+
+    private async Task DoSaveNewPost(NewsEntry post)
+    {
+        NewsEntryRepository.Add(post);
+        
+        NavigationManager.NavigateTo(NavigationManager.Uri, true);
+    }
+
+    private async Task Delete(NewsEntry entry)
+    {
+        var confirm = await AlertService.YesNo(
+            SmartTranslateService.Translate("Delete post"),  
+            SmartTranslateService.Translate("Do you really want to delete the post \"") + entry.Title + "\"?", 
+            SmartTranslateService.Translate("Yes"),
+            SmartTranslateService.Translate("No")
+        );
+        
+        if(!confirm) return;
+        
+        NewsEntryRepository.Delete(entry);
+        
+        NavigationManager.NavigateTo(NavigationManager.Uri, true);
+    }
+}

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

@@ -550,6 +550,13 @@ Website details;Website details
 Configure your website;Configure your website
 The name cannot be longer that 32 characters;The name cannot be longer that 32 characters
 The name should only consist of lower case characters;The name should only consist of lower case characters
+News;News
+Title...;Title...
+Enter text...;Enter text...
+Saving...;Saving...
+Deleting...;Deleting...
+Delete post;Delete post
+Do you really want to delete the post ";Do you really want to delete the post "
 You have no domains;You have no domains
 We were not able to find any domains associated with your account;We were not able to find any domains associated with your account
 You have no websites;You have no websites