Merge pull request #71 from Moonlight-Panel/main

Update DiscordBot branch
This commit is contained in:
Spielepapagei 2023-04-15 19:33:29 +02:00 committed by GitHub
commit aba61257d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
151 changed files with 12892 additions and 1754 deletions

4
.gitignore vendored
View file

@ -38,4 +38,6 @@ _UpgradeReport_Files/
Thumbs.db
Desktop.ini
.DS_Store
.idea/.idea.Moonlight/.idea/discord.xml
.idea/.idea.Moonlight/.idea/discord.xml
storage/

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -18,7 +18,6 @@ public class DataContext : DbContext
public DbSet<DockerImage> DockerImages { get; set; }
public DbSet<Image> Images { get; set; }
public DbSet<ImageTag> ImageTags { get; set; }
public DbSet<ImageVariable> ImageVariables { get; set; }
public DbSet<Node> Nodes { get; set; }
public DbSet<NodeAllocation> NodeAllocations { get; set; }
@ -39,6 +38,10 @@ public class DataContext : DbContext
public DbSet<NotificationAction> NotificationActions { get; set; }
public DbSet<DdosAttack> DdosAttacks { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
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)
{

View file

@ -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; }
}

View file

@ -1,7 +1,9 @@
namespace Moonlight.App.Database.Entities;
public class ImageTag
public class PleskServer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string ApiUrl { get; set; } = "";
public string ApiKey { get; set; } = "";
}

View file

@ -17,7 +17,7 @@ public class Server
public List<ServerVariable> Variables { get; set; } = new();
public List<ServerBackup> Backups { get; set; } = new();
public List<NodeAllocation> Allocations { get; set; } = new();
public NodeAllocation MainAllocation { get; set; } = null!;
public NodeAllocation? MainAllocation { get; set; } = null;
public Node Node { get; set; } = null!;
public User Owner { get; set; } = null!;
public bool IsCleanupException { get; set; } = false;

View file

@ -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; }
}

View file

@ -0,0 +1,12 @@
namespace Moonlight.App.Database.Entities;
public class Website
{
public int Id { get; set; }
public string BaseDomain { get; set; } = "";
public int PleskId { get; set; }
public PleskServer PleskServer { get; set; }
public User Owner { get; set; }
public string FtpLogin { get; set; } = "";
public string FtpPassword { get; set; } = "";
}

View file

@ -0,0 +1,946 @@
// <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("20230404181522_AddedPleskAndWebsiteModels")]
partial class AddedPleskAndWebsiteModels
{
/// <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.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<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
}
}
}

View file

@ -0,0 +1,84 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedPleskAndWebsiteModels : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PleskServers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ApiUrl = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ApiKey = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_PleskServers", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Websites",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
BaseDomain = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
PleskId = table.Column<int>(type: "int", nullable: false),
PleskServerId = table.Column<int>(type: "int", nullable: false),
OwnerId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Websites", x => x.Id);
table.ForeignKey(
name: "FK_Websites_PleskServers_PleskServerId",
column: x => x.PleskServerId,
principalTable: "PleskServers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Websites_Users_OwnerId",
column: x => x.OwnerId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Websites_OwnerId",
table: "Websites",
column: "OwnerId");
migrationBuilder.CreateIndex(
name: "IX_Websites_PleskServerId",
table: "Websites",
column: "PleskServerId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Websites");
migrationBuilder.DropTable(
name: "PleskServers");
}
}
}

View file

@ -0,0 +1,954 @@
// <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("20230405162507_UpdatedWebsiteModel")]
partial class UpdatedWebsiteModel
{
/// <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.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
}
}
}

View file

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class UpdatedWebsiteModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "FtpLogin",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "FtpPassword",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "FtpLogin",
table: "Websites");
migrationBuilder.DropColumn(
name: "FtpPassword",
table: "Websites");
}
}
}

View file

@ -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
}
}
}

View file

@ -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");
}
}
}

View file

@ -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
}
}
}

View file

@ -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");
}
}
}

View file

@ -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
}
}
}

View file

@ -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");
}
}
}

View file

@ -0,0 +1,995 @@
// <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("20230413150426_MadeMainAllocationNullable")]
partial class MadeMainAllocationNullable
{
/// <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");
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
}
}
}

View file

@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class MadeMainAllocationNullable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Servers_NodeAllocations_MainAllocationId",
table: "Servers");
migrationBuilder.AlterColumn<int>(
name: "MainAllocationId",
table: "Servers",
type: "int",
nullable: true,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AddForeignKey(
name: "FK_Servers_NodeAllocations_MainAllocationId",
table: "Servers",
column: "MainAllocationId",
principalTable: "NodeAllocations",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Servers_NodeAllocations_MainAllocationId",
table: "Servers");
migrationBuilder.AlterColumn<int>(
name: "MainAllocationId",
table: "Servers",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "int",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_Servers_NodeAllocations_MainAllocationId",
table: "Servers",
column: "MainAllocationId",
principalTable: "NodeAllocations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View file

@ -0,0 +1,980 @@
// <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("20230414110513_RemovedImageTags")]
partial class RemovedImageTags
{
/// <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.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");
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
}
}
}

View file

@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class RemovedImageTags : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ImageTags");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ImageTags",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_ImageTags", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
}
}

View file

@ -153,21 +153,6 @@ namespace Moonlight.App.Database.Migrations
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")
@ -296,6 +281,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")
@ -395,6 +402,29 @@ namespace Moonlight.App.Database.Migrations
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")
@ -434,7 +464,7 @@ namespace Moonlight.App.Database.Migrations
b.Property<bool>("IsCleanupException")
.HasColumnType("tinyint(1)");
b.Property<int>("MainAllocationId")
b.Property<int?>("MainAllocationId")
.HasColumnType("int");
b.Property<long>("Memory")
@ -548,6 +578,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")
@ -642,8 +693,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()
@ -697,6 +748,42 @@ namespace Moonlight.App.Database.Migrations
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")
@ -784,9 +871,7 @@ namespace Moonlight.App.Database.Migrations
b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation")
.WithMany()
.HasForeignKey("MainAllocationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.HasForeignKey("MainAllocationId");
b.HasOne("Moonlight.App.Database.Entities.Node", "Node")
.WithMany()
@ -847,6 +932,25 @@ namespace Moonlight.App.Database.Migrations
b.Navigation("CurrentSubscription");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer")
.WithMany()
.HasForeignKey("PleskServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("PleskServer");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
{
b.Navigation("DockerImages");

View file

@ -0,0 +1,32 @@
using System.Runtime.Serialization;
namespace Moonlight.App.Exceptions;
[Serializable]
public class DaemonException : Exception
{
public int StatusCode { private get; set; }
public DaemonException()
{
}
public DaemonException(string message, int statusCode) : base(message)
{
StatusCode = statusCode;
}
public DaemonException(string message) : base(message)
{
}
public DaemonException(string message, Exception inner) : base(message, inner)
{
}
protected DaemonException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}

View file

@ -0,0 +1,32 @@
using System.Runtime.Serialization;
namespace Moonlight.App.Exceptions;
[Serializable]
public class PleskException : Exception
{
public int StatusCode { private get; set; }
public PleskException()
{
}
public PleskException(string message, int statusCode) : base(message)
{
StatusCode = statusCode;
}
public PleskException(string message) : base(message)
{
}
public PleskException(string message, Exception inner) : base(message, inner)
{
}
protected PleskException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}

View file

@ -16,11 +16,13 @@ public class DaemonApiHelper
private string GetApiUrl(Node node)
{
/* SSL not implemented in moonlight daemon
if(node.Ssl)
return $"https://{node.Fqdn}:{node.MoonlightDaemonPort}/";
else
return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";
//return $"https://{node.Fqdn}:{node.HttpPort}/";
return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";*/
return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";
}
public async Task<T> Get<T>(Node node, string resource)
@ -37,7 +39,7 @@ public class DaemonApiHelper
{
if (response.StatusCode != 0)
{
throw new WingsException(
throw new DaemonException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);

View file

@ -0,0 +1,182 @@
using System.Net;
using System.Text;
using FluentFTP;
using Moonlight.App.Exceptions;
namespace Moonlight.App.Helpers.Files;
public class FtpFileAccess : FileAccess
{
private string FtpHost, FtpUser, FtpPassword;
private int FtpPort;
private AsyncFtpClient Client;
public FtpFileAccess(string ftpHost, int ftpPort, string ftpUser, string ftpPassword)
{
FtpHost = ftpHost;
FtpPort = ftpPort;
FtpUser = ftpUser;
FtpPassword = ftpPassword;
Client = new AsyncFtpClient(FtpHost, FtpUser, FtpPassword, FtpPort);
}
private async Task EnsureConnect()
{
if (!Client.IsConnected)
await Client.AutoConnect();
}
public override async Task<FileData[]> Ls()
{
await EnsureConnect();
var x = new List<FileData>();
foreach (FtpListItem item in (await Client.GetListing(CurrentPath)).OrderBy(x => x.Type + " " + x.Name))
{
long size = 0;
if (item.Type == FtpObjectType.File)
{
size = await Client.GetFileSize(item.FullName);
}
x.Add(new()
{
Name = item.Name,
Size = size,
IsFile = item.Type == FtpObjectType.File,
});
}
return x.ToArray();
}
public override Task Cd(string dir)
{
var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/";
x = x.Replace("//", "/");
CurrentPath = x;
return Task.CompletedTask;
}
public override Task Up()
{
CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", "");
return Task.CompletedTask;
}
public override Task SetDir(string dir)
{
CurrentPath = dir;
return Task.CompletedTask;
}
public override async Task<string> Read(FileData fileData)
{
await EnsureConnect();
var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option
await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name);
var data = s.ToArray();
await s.DisposeAsync();
var str = Encoding.UTF8.GetString(data);
return str;
}
public override async Task Write(FileData fileData, string content)
{
await EnsureConnect();
var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option
s.Write(Encoding.UTF8.GetBytes(content));
s.Position = 0;
await Client.UploadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name, FtpRemoteExists.Overwrite);
await s.DisposeAsync();
}
public override async Task Upload(string name, Stream dataStream, Action<int>? progressUpdated = null)
{
await EnsureConnect();
IProgress<FtpProgress> progress = new Progress<FtpProgress>(x =>
{
progressUpdated?.Invoke((int)x.Progress);
});
await Client.UploadStream(dataStream, CurrentPath.TrimEnd('/') + "/" + name, FtpRemoteExists.Overwrite, false, progress);
}
public override async Task MkDir(string name)
{
await EnsureConnect();
await Client.CreateDirectory(CurrentPath.TrimEnd('/') + "/" + 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)
{
await EnsureConnect();
var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option
var downloaded = await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name);
if (!downloaded)
throw new DisplayException("Unable to download file");
return s;
}
public override async Task Delete(FileData fileData)
{
await EnsureConnect();
if (fileData.IsFile)
await Client.DeleteFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name);
else
await Client.DeleteDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name);
}
public override async Task Move(FileData fileData, string newPath)
{
await EnsureConnect();
if (fileData.IsFile)
await Client.MoveFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath);
else
await Client.MoveDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath);
}
public override Task Compress(params FileData[] files)
{
throw new NotImplementedException();
}
public override Task Decompress(FileData fileData)
{
throw new NotImplementedException();
}
public override Task<string> GetLaunchUrl()
{
return Task.FromResult(
$"ftp://{FtpUser}:{FtpPassword}@{FtpHost}:{FtpPort}/");
}
public override object Clone()
{
return new FtpFileAccess(FtpHost, FtpPort, FtpUser, FtpPassword);
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,28 @@
namespace Moonlight.App.Helpers;
public class PathBuilder
{
public static string Dir(params string[] parts)
{
var res = "";
foreach (var part in parts)
{
res += part + Path.DirectorySeparatorChar;
}
return res;
}
public static string File(params string[] parts)
{
var res = "";
foreach (var part in parts)
{
res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar);
}
return res;
}
}

View file

@ -0,0 +1,220 @@
using System.Text;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Newtonsoft.Json;
using RestSharp;
namespace Moonlight.App.Helpers;
public class PleskApiHelper
{
private readonly RestClient Client;
public PleskApiHelper()
{
Client = new();
}
public async Task<T> Get<T>(PleskServer server, string resource)
{
var request = CreateRequest(server, resource);
request.Method = Method.Get;
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
return JsonConvert.DeserializeObject<T>(response.Content!)!;
}
public async Task<string> GetRaw(PleskServer server, string resource)
{
var request = CreateRequest(server, resource);
request.Method = Method.Get;
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
return response.Content!;
}
public async Task<T> Post<T>(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
request.Method = Method.Post;
request.AddParameter("text/plain",
JsonConvert.SerializeObject(body),
ParameterType.RequestBody
);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
return JsonConvert.DeserializeObject<T>(response.Content!)!;
}
public async Task Post(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
request.Method = Method.Post;
if(body != null)
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
}
public async Task PostRaw(PleskServer server, string resource, object body)
{
var request = CreateRequest(server, resource);
request.Method = Method.Post;
request.AddParameter("text/plain", body, ParameterType.RequestBody);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
}
public async Task Delete(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
request.Method = Method.Delete;
if(body != null)
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
}
public async Task Put(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
request.Method = Method.Put;
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
}
private RestRequest CreateRequest(PleskServer pleskServer, string resource)
{
var url = $"{pleskServer.ApiUrl}/" + resource;
var request = new RestRequest(url);
var ba = Convert.ToBase64String(Encoding.UTF8.GetBytes(pleskServer.ApiKey));
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Basic " + ba);
return request;
}
}

View file

@ -8,7 +8,7 @@ public class SmartTranslateHelper
{
Languages = new();
foreach (var file in Directory.GetFiles("resources/lang"))
foreach (var file in Directory.GetFiles(PathBuilder.Dir("storage", "resources", "lang")))
{
if (Path.GetExtension(file) == ".lang")
{
@ -21,7 +21,9 @@ public class SmartTranslateHelper
foreach (var line in lines)
{
var parts = line.Split(";");
content.Add(parts[0], parts[1]);
if(!content.ContainsKey(parts[0]))
content.Add(parts[0], parts[1]);
}
Languages.Add(langKey, content);
@ -38,7 +40,7 @@ public class SmartTranslateHelper
{
Languages[langKey].Add(content, content);
File.WriteAllLines($"resources/lang/{langKey}.lang", GenerateData(Languages[langKey]));
File.WriteAllLines(PathBuilder.File("storage", "resources", "lang", $"{langKey}.lang"), GenerateData(Languages[langKey]));
}
return Languages[langKey][content];

View file

@ -46,13 +46,14 @@ public class WingsServerConverter
}
// Build
wingsServer.Settings.Build.Swap = server.Memory * 2;
wingsServer.Settings.Build.Swap = server.Memory * 2; //TODO: Add config option
wingsServer.Settings.Build.Threads = null!;
wingsServer.Settings.Build.Cpu_Limit = server.Cpu;
wingsServer.Settings.Build.Disk_Space = server.Disk;
wingsServer.Settings.Build.Io_Weight = 500;
wingsServer.Settings.Build.Memory_Limit = server.Memory;
wingsServer.Settings.Build.Oom_Disabled = true;
wingsServer.Settings.Build.Oom_Killer = false;
var image = ImageRepository
.Get()
@ -117,12 +118,15 @@ public class WingsServerConverter
foreach (var section in child.GetSection("find").GetChildren())
{
replaces.Add(new()
if (section.Value != null)
{
Match = section.Key,
Replace_With = section.Value
.Replace("{{server.build.default.port}}", def.Port.ToString())
});
replaces.Add(new()
{
Match = section.Key,
Replace_With = section.Value
.Replace("{{server.build.default.port}}", def.Port.ToString())
});
}
}
wingsServer.Process_Configuration.Configs.Add(new()

View file

@ -58,7 +58,7 @@ public class OAuth2Controller : Controller
}
else
{
token = await UserService.GenerateToken(user);
token = await UserService.GenerateToken(user, true);
}
Response.Cookies.Append("token", token, new ()
@ -116,7 +116,7 @@ public class OAuth2Controller : Controller
}
else
{
token = await UserService.GenerateToken(user);
token = await UserService.GenerateToken(user, true);
}
Response.Cookies.Append("token", token, new ()

View file

@ -1,5 +1,6 @@
using Logging.Net;
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Misc;
using Moonlight.App.Services.LogServices;
@ -28,14 +29,13 @@ public class ResourcesController : Controller
return NotFound();
}
if (System.IO.File.Exists($"resources/public/images/{name}"))
if (System.IO.File.Exists(PathBuilder.File("storage", "resources", "public", "images", name)))
{
var fs = new FileStream($"resources/public/images/{name}", FileMode.Open);
var fs = new FileStream(PathBuilder.File("storage", "resources", "public", "images", name), FileMode.Open);
return File(fs, MimeTypes.GetMimeType(name), name);
}
Logger.Debug("404 on resources");
return NotFound();
}
}

View file

@ -138,7 +138,15 @@ public class ServersController : Controller
await MessageService.Emit($"wings.{node.Id}.serverfetch", server);
return Converter.FromServer(server);
try //TODO: Remove
{
return Converter.FromServer(server);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
[HttpGet("{uuid}/install")]

View file

@ -130,6 +130,9 @@ public class WingsServer
[JsonPropertyName("oom_disabled")]
public bool Oom_Disabled { get; set; }
[JsonPropertyName("oom_killer")]
public bool Oom_Killer { get; set; }
}
public class WingsServerContainer

View file

@ -1,145 +0,0 @@
using Logging.Net;
using Moonlight.App.Exceptions;
using Moonlight.App.Helpers;
namespace Moonlight.App.Models.Files.Accesses;
public class HostFileAccess : IFileAccess
{
private readonly string BasePath;
private string RealPath => BasePath + Path;
private string Path = "/";
public HostFileAccess(string bp)
{
BasePath = bp;
}
public Task<FileManagerObject[]> GetDirectoryContent()
{
var x = new List<FileManagerObject>();
foreach (var directory in Directory.EnumerateDirectories(RealPath))
{
x.Add(new ()
{
Name = System.IO.Path.GetFileName(directory)!,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
Size = 0,
IsFile = false
});
}
foreach (var file in Directory.GetFiles(RealPath))
{
x.Add(new ()
{
Name = System.IO.Path.GetFileName(file)!,
CreatedAt = File.GetCreationTimeUtc(file),
UpdatedAt = File.GetLastWriteTimeUtc(file),
Size = new System.IO.FileInfo(file).Length,
IsFile = File.Exists(file)
});
}
return Task.FromResult(x.ToArray());
}
public Task ChangeDirectory(string s)
{
var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/";
x = x.Replace("//", "/");
Path = x;
return Task.CompletedTask;
}
public Task SetDirectory(string s)
{
Path = s;
return Task.CompletedTask;
}
public Task GoUp()
{
Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", "");
return Task.CompletedTask;
}
public Task<string> ReadFile(FileManagerObject fileManagerObject)
{
return Task.FromResult(File.ReadAllText(RealPath + fileManagerObject.Name));
}
public Task WriteFile(FileManagerObject fileManagerObject, string content)
{
File.WriteAllText(RealPath + fileManagerObject.Name, content);
return Task.CompletedTask;
}
public async Task UploadFile(string name, Stream stream, Action<int>? progressUpdated = null)
{
var fs = File.OpenWrite(RealPath + name);
var dataStream = new StreamProgressHelper(stream)
{
Progress = i =>
{
if (progressUpdated != null)
progressUpdated.Invoke(i);
}
};
await dataStream.CopyToAsync(fs);
await fs.FlushAsync();
fs.Close();
}
public Task CreateDirectory(string name)
{
Directory.CreateDirectory(RealPath + name);
return Task.CompletedTask;
}
public Task<string> GetCurrentPath()
{
return Task.FromResult(Path);
}
public Task<Stream> GetDownloadStream(FileManagerObject managerObject)
{
var stream = new FileStream(RealPath + managerObject.Name, FileMode.Open, FileAccess.Read);
return Task.FromResult<Stream>(stream);
}
public Task<string> GetDownloadUrl(FileManagerObject managerObject)
{
throw new NotImplementedException();
}
public Task Delete(FileManagerObject managerObject)
{
if(managerObject.IsFile)
File.Delete(RealPath + managerObject.Name);
else
Directory.Delete(RealPath + managerObject.Name, true);
return Task.CompletedTask;
}
public Task Move(FileManagerObject managerObject, string newPath)
{
if(managerObject.IsFile)
File.Move(RealPath + managerObject.Name, BasePath + newPath);
else
Directory.Move(RealPath + managerObject.Name, BasePath + newPath);
return Task.CompletedTask;
}
public Task<string> GetLaunchUrl()
{
throw new DisplayException("WinSCP cannot be launched here");
}
}

View file

@ -1,205 +0,0 @@
using System.Security.Cryptography;
using System.Text;
using System.Web;
using JWT.Algorithms;
using JWT.Builder;
using Moonlight.App.Database.Entities;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Wings.Requests;
using Moonlight.App.Models.Wings.Resources;
using RestSharp;
namespace Moonlight.App.Models.Files.Accesses;
public class WingsFileAccess : IFileAccess
{
private readonly WingsApiHelper WingsApiHelper;
private readonly WingsJwtHelper WingsJwtHelper;
private readonly Database.Entities.Node Node;
private readonly Server Server;
private readonly User User;
private readonly string AppUrl;
private string Path { get; set; } = "/";
public WingsFileAccess(
WingsApiHelper wingsApiHelper,
Server server,
User user,
WingsJwtHelper wingsJwtHelper,
string appUrl)
{
WingsApiHelper = wingsApiHelper;
Node = server.Node;
Server = server;
User = user;
WingsJwtHelper = wingsJwtHelper;
AppUrl = appUrl;
}
public async Task<FileManagerObject[]> GetDirectoryContent()
{
var res = await WingsApiHelper.Get<ListDirectory[]>(Node,
$"api/servers/{Server.Uuid}/files/list-directory?directory={Path}");
var x = new List<FileManagerObject>();
foreach (var response in res)
{
x.Add(new()
{
Name = response.Name,
Size = response.File ? response.Size : 0,
CreatedAt = response.Created.Date,
IsFile = response.File,
UpdatedAt = response.Modified.Date
});
}
return x.ToArray();
}
public Task ChangeDirectory(string s)
{
var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/";
x = x.Replace("//", "/");
Path = x;
return Task.CompletedTask;
}
public Task SetDirectory(string s)
{
Path = s;
return Task.CompletedTask;
}
public Task GoUp()
{
Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", "");
return Task.CompletedTask;
}
public async Task<string> ReadFile(FileManagerObject fileManagerObject)
{
return await WingsApiHelper.GetRaw(Node,
$"api/servers/{Server.Uuid}/files/contents?file={Path}{fileManagerObject.Name}");
}
public async Task WriteFile(FileManagerObject fileManagerObject, string content)
{
await WingsApiHelper.PostRaw(Node,
$"api/servers/{Server.Uuid}/files/write?file={Path}{fileManagerObject.Name}", content);
}
public async Task UploadFile(string name, Stream dataStream, Action<int> onProgress)
{
var token = WingsJwtHelper.Generate(Node.Token,
claims => { claims.Add("server_uuid", Server.Uuid.ToString()); });
var client = new RestClient();
var request = new RestRequest();
if (Node.Ssl)
request.Resource = $"https://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}";
else
request.Resource = $"http://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}";
request.AddParameter("name", "files");
request.AddParameter("filename", name);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Origin", AppUrl);
request.AddFile("files", () =>
{
return new StreamProgressHelper(dataStream)
{
Progress = i =>
{
if (onProgress != null)
onProgress.Invoke(i);
}
};
}, name);
await client.ExecutePostAsync(request);
client.Dispose();
dataStream.Close();
}
public async Task CreateDirectory(string name)
{
await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/create-directory",
new CreateDirectory()
{
Name = name,
Path = Path
});
}
public Task<string> GetCurrentPath()
{
return Task.FromResult(Path);
}
public Task<Stream> GetDownloadStream(FileManagerObject managerObject)
{
throw new NotImplementedException();
}
public Task<string> GetDownloadUrl(FileManagerObject managerObject)
{
var token = WingsJwtHelper.Generate(Node.Token, claims =>
{
claims.Add("server_uuid", Server.Uuid.ToString());
claims.Add("file_path", HttpUtility.UrlDecode(Path + "/" + managerObject.Name));
});
if (Node.Ssl)
{
return Task.FromResult(
$"https://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}"
);
}
else
{
return Task.FromResult(
$"http://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}"
);
}
}
public async Task Delete(FileManagerObject managerObject)
{
await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFiles()
{
Root = Path,
Files = new()
{
managerObject.Name
}
});
}
public async Task Move(FileManagerObject managerObject, string newPath)
{
await WingsApiHelper.Put(Node, $"api/servers/{Server.Uuid}/files/rename", new RenameFiles()
{
Root = "/",
Files = new[]
{
new RenameFilesData()
{
From = Path + managerObject.Name,
To = newPath
}
}
});
}
public Task<string> GetLaunchUrl()
{
return Task.FromResult(
$"sftp://{User.Id}.{StringHelper.IntToStringWithLeadingZeros(Server.Id, 8)}@{Node.Fqdn}:{Node.SftpPort}");
}
}

View file

@ -1,8 +0,0 @@
namespace Moonlight.App.Models.Files;
public class FileContextAction
{
public string Id { get; set; }
public string Name { get; set; }
public Action<FileManagerObject> Action { get; set; }
}

View file

@ -1,10 +0,0 @@
namespace Moonlight.App.Models.Files;
public class FileManagerObject
{
public string Name { get; set; }
public bool IsFile { get; set; }
public long Size { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View file

@ -1,19 +0,0 @@
namespace Moonlight.App.Models.Files;
public interface IFileAccess
{
public Task<FileManagerObject[]> GetDirectoryContent();
public Task ChangeDirectory(string s);
public Task SetDirectory(string s);
public Task GoUp();
public Task<string> ReadFile(FileManagerObject fileManagerObject);
public Task WriteFile(FileManagerObject fileManagerObject, string content);
public Task UploadFile(string name, Stream stream, Action<int> progressUpdated);
public Task CreateDirectory(string name);
public Task<string> GetCurrentPath();
public Task<Stream> GetDownloadStream(FileManagerObject managerObject);
public Task<string> GetDownloadUrl(FileManagerObject managerObject);
public Task Delete(FileManagerObject managerObject);
public Task Move(FileManagerObject managerObject, string newPath);
public Task<string> GetLaunchUrl();
}

View file

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class DatabaseDataModel
{
[Required(ErrorMessage = "You need to enter a name")]
[MinLength(8, ErrorMessage = "The name should be at least 8 characters long")]
[MaxLength(32, ErrorMessage = "The database name should be maximal 32 characters")]
[RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only contain of lower case characters and numbers")]
public string Name { get; set; } = "";
[Required(ErrorMessage = "You need to enter a password")]
[MinLength(8, ErrorMessage = "The password should be at least 8 characters long")]
[MaxLength(32, ErrorMessage = "The password name should be maximal 32 characters")]
public string Password { get; set; } = "";
}

View file

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Forms;
public class DomainDataModel
{
[Required(ErrorMessage = "You need to specify a name")]
[MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")]
[RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")]
public string Name { get; set; } = "";
[Required(ErrorMessage = "You need to specify a shared domain")]
public SharedDomain SharedDomain { get; set; }
[Required(ErrorMessage = "You need to specify a owner")]
public User Owner { get; set; }
}

View file

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Forms;
public class DomainOrderDataModel
{
[Required(ErrorMessage = "You need to specify a name")]
[MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")]
[RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")]
public string Name { get; set; } = "";
[Required(ErrorMessage = "You need to specify a shared domain")]
public SharedDomain SharedDomain { get; set; }
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class PleskServerDataModel
{
[Required(ErrorMessage = "You have to enter a name")]
[MaxLength(32, ErrorMessage = "The name should not be longer than 32 characters")]
public string Name { get; set; }
[Required(ErrorMessage = "You need to enter an api url")]
public string ApiUrl { get; set; }
[Required(ErrorMessage = "You need to enter an api key")]
public string ApiKey { get; set; }
}

View file

@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Forms;
public class ServerDataModel
{
[Required(ErrorMessage = "You need to enter a name")]
[MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")]
public string Name { get; set; }
[Required(ErrorMessage = "You need to specify a owner")]
public User Owner { get; set; }
[Required(ErrorMessage = "You need to specify cpu amount")]
public int Cpu { get; set; } = 100;
[Required(ErrorMessage = "You need to specify a memory amount")]
public int Memory { get; set; } = 1024;
[Required(ErrorMessage = "You need to specify a disk amount")]
public int Disk { get; set; } = 1024;
[Required(ErrorMessage = "You need to specify a image")]
public Image Image { get; set; }
public string OverrideStartup { get; set; } = "";
public int DockerImageIndex { get; set; }
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Forms;
public class TestDataModel
{
[Required]
public User User { get; set; }
}

View file

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class UserDataModel
{
[Required]
public string FirstName { get; set; } = "";
[Required]
public string LastName { get; set; } = "";
[Required]
public string Email { get; set; } = "";
[Required]
public string Address { get; set; } = "";
[Required]
public string City { get; set; } = "";
[Required]
public string State { get; set; } = "";
[Required]
public string Country { get; set; } = "";
}

View file

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class UserRegisterModel
{
[Required, EmailAddress]
public string Email { get; set; }
[Required, MinLength(3)]
public string FirstName { get; set; }
[Required, MinLength(3)]
public string LastName { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string ConfirmPassword { get; set; }
}

View file

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Forms;
public class WebsiteAdminDataModel
{
[Required(ErrorMessage = "You need a domain")]
[RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")]
public string BaseDomain { get; set; } = "";
[Required(ErrorMessage = "You need to specify a owner")]
public User User { get; set; }
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class WebsiteDataModel
{
[Required(ErrorMessage = "You need to enter a domain")]
[RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")]
public string BaseDomain { get; set; } = "";
}

View file

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class WebsiteOrderDataModel
{
[Required(ErrorMessage = "You need to enter a domain")]
[RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")]
public string BaseDomain { get; set; } = "";
}

View file

@ -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
}

View file

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Requests;
public class CliCall
{
[JsonProperty("params")] public List<string> Params { get; set; } = new();
[JsonProperty("env")] public Dictionary<string, string> Env { get; set; } = new();
}

View file

@ -0,0 +1,23 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Requests;
public class CreateDatabase
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("parent_domain")] public ParentDomainModel ParentDomain { get; set; } = new();
[JsonProperty("server_id")]
public int ServerId { get; set; }
public class ParentDomainModel
{
[JsonProperty("name")]
public string Name { get; set; }
}
}

View file

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Requests;
public class CreateDatabaseUser
{
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("password")]
public string Password { get; set; }
[JsonProperty("database_id")]
public int DatabaseId { get; set; }
}

View file

@ -0,0 +1,45 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Requests;
public class CreateDomain
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("hosting_type")]
public string HostingType { get; set; }
[JsonProperty("hosting_settings")]
public HostingSettingsModel HostingSettings { get; set; }
[JsonProperty("owner_client")]
public OwnerClientModel OwnerClient { get; set; }
[JsonProperty("plan")]
public PlanModel Plan { get; set; }
public partial class HostingSettingsModel
{
[JsonProperty("ftp_login")]
public string FtpLogin { get; set; }
[JsonProperty("ftp_password")]
public string FtpPassword { get; set; }
}
public partial class OwnerClientModel
{
[JsonProperty("id")]
public long Id { get; set; }
}
public partial class PlanModel
{
[JsonProperty("name")]
public string Name { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class CliResult
{
[JsonProperty("code")]
public int Code { get; set; }
[JsonProperty("stdout")] public string Stdout { get; set; } = "";
[JsonProperty("stderr")] public string Stderr { get; set; } = "";
}

View file

@ -0,0 +1,45 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class Client
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("created")]
public DateTimeOffset Created { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("company")]
public string Company { get; set; }
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("locale")]
public string Locale { get; set; }
[JsonProperty("guid")]
public Guid Guid { get; set; }
[JsonProperty("owner_login")]
public string OwnerLogin { get; set; }
[JsonProperty("external_id")]
public string ExternalId { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}

View file

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class CreateResult
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("guid")]
public Guid Guid { get; set; }
}

View file

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class Database
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}

View file

@ -0,0 +1,30 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class DatabaseServer
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("host")]
public string Host { get; set; }
[JsonProperty("port")]
public int Port { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("db_count")]
public int DbCount { get; set; }
[JsonProperty("is_default")]
public bool IsDefault { get; set; }
[JsonProperty("is_local")]
public bool IsLocal { get; set; }
}

View file

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class DatabaseUser
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("database_id")]
public int DatabaseId { get; set; }
}

View file

@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class ServerStatus
{
[JsonProperty("platform")]
public string Platform { get; set; }
[JsonProperty("hostname")]
public string Hostname { get; set; }
[JsonProperty("guid")]
public Guid Guid { get; set; }
[JsonProperty("panel_version")]
public string PanelVersion { get; set; }
}

View file

@ -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();
}
}

View file

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

View file

@ -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();
}
}

View file

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

View file

@ -7,26 +7,44 @@ namespace Moonlight.App.Services;
public class ConfigService : IConfiguration
{
private readonly StorageService StorageService;
private IConfiguration Configuration;
public bool DebugMode { get; private set; } = false;
public ConfigService()
public ConfigService(StorageService storageService)
{
Configuration = new ConfigurationBuilder().AddJsonStream(
new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText("..\\..\\appsettings.json")))
).Build();
StorageService = storageService;
StorageService.EnsureCreated();
Reload();
// Env vars
var debugVar = Environment.GetEnvironmentVariable("ML_DEBUG");
if (debugVar != null)
DebugMode = bool.Parse(debugVar);
if(DebugMode)
if (DebugMode)
Logger.Debug("Debug mode enabled");
}
public void Reload()
{
Logger.Info($"Reading config from '{PathBuilder.File("storage", "configs", "config.json")}'");
Configuration = new ConfigurationBuilder().AddJsonStream(
new MemoryStream(Encoding.ASCII.GetBytes(
File.ReadAllText(
PathBuilder.File("storage", "configs", "config.json")
)
)
)).Build();
Logger.Info("Reloaded configuration file");
}
public IEnumerable<IConfigurationSection> GetChildren()
{
return Configuration.GetChildren();

View file

@ -48,6 +48,28 @@ public class DomainService
);
}
public Task<Domain> Create(string domain, SharedDomain sharedDomain, User user)
{
if (DomainRepository.Get().Where(x => x.SharedDomain.Id == sharedDomain.Id).Any(x => x.Name == domain))
throw new DisplayException("A domain with this name does already exist for this shared domain");
var res = DomainRepository.Add(new()
{
Name = domain,
SharedDomain = sharedDomain,
Owner = user
});
return Task.FromResult(res);
}
public Task Delete(Domain domain)
{
DomainRepository.Delete(domain);
return Task.CompletedTask;
}
public async Task<Zone[]>
GetAvailableDomains() // This method returns all available domains which are not added as a shared domain
{

View file

@ -73,8 +73,11 @@ public class AuditLogService
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
public void Add<T>(object? data)
{
if(data == null)
return;
Data.Add(new LogData()
{
Type = typeof(T),

View file

@ -98,8 +98,11 @@ public class ErrorLogService
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
public void Add<T>(object? data)
{
if(data == null)
return;
Data.Add(new LogData()
{
Type = typeof(T),

View file

@ -72,8 +72,11 @@ public class SecurityLogService
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
public void Add<T>(object? data)
{
if(data == null)
return;
Data.Add(new LogData()
{
Type = typeof(T),

View file

@ -3,6 +3,7 @@ using System.Net.Mail;
using Logging.Net;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Moonlight.App.Helpers;
namespace Moonlight.App.Services;
@ -31,13 +32,13 @@ public class MailService
Action<Dictionary<string, string>> values
)
{
if (!File.Exists($"resources/mail/{name}.html"))
if (!File.Exists(PathBuilder.File("storage", "resources", "mail", $"{name}.html")))
{
Logger.Warn($"Mail template '{name}' not found. Make sure to place one in the resources folder");
throw new DisplayException("Mail template not found");
}
var rawHtml = await File.ReadAllTextAsync($"resources/mail/{name}.html");
var rawHtml = await File.ReadAllTextAsync(PathBuilder.File("storage", "resources", "mail", $"{name}.html"));
var val = new Dictionary<string, string>();
values.Invoke(val);

View file

@ -41,4 +41,22 @@ public class NodeService
{
return await DaemonApiHelper.Get<ContainerStats>(node, "stats/container");
}
public async Task<bool> IsHostUp(Node node)
{
try
{
//TODO: Implement status caching
var data = await GetStatus(node);
if (data != null)
return true;
}
catch (Exception)
{
// ignored
}
return false;
}
}

View file

@ -29,6 +29,7 @@ public class ServerService
private readonly SecurityLogService SecurityLogService;
private readonly AuditLogService AuditLogService;
private readonly ErrorLogService ErrorLogService;
private readonly NodeService NodeService;
public ServerService(
ServerRepository serverRepository,
@ -42,7 +43,8 @@ public class ServerService
WingsJwtHelper wingsJwtHelper,
SecurityLogService securityLogService,
AuditLogService auditLogService,
ErrorLogService errorLogService)
ErrorLogService errorLogService,
NodeService nodeService)
{
ServerRepository = serverRepository;
WingsApiHelper = wingsApiHelper;
@ -56,6 +58,7 @@ public class ServerService
SecurityLogService = securityLogService;
AuditLogService = auditLogService;
ErrorLogService = errorLogService;
NodeService = nodeService;
}
private Server EnsureNodeData(Server s)
@ -192,13 +195,14 @@ public class ServerService
ServerRepository.Update(serverData);
await MessageService.Emit("wings.backups.delete", backup);
await AuditLogService.Log(AuditLogType.DeleteBackup,
x =>
{
x.Add<Server>(server.Uuid);
x.Add<ServerBackup>(backup.Uuid);
});
}
);
}
public async Task<string> DownloadBackup(Server s, ServerBackup serverBackup)
@ -210,7 +214,7 @@ public class ServerService
claims.Add("server_uuid", server.Uuid.ToString());
claims.Add("backup_uuid", serverBackup.Uuid.ToString());
});
await AuditLogService.Log(AuditLogType.DownloadBackup,
x =>
{
@ -218,7 +222,10 @@ public class ServerService
x.Add<ServerBackup>(serverBackup.Uuid);
});
return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
if (server.Node.Ssl)
return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
else
return $"http://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
}
public Task<FileAccess> CreateFileAccess(Server s, User user) // We need the user to create the launch url
@ -237,7 +244,7 @@ public class ServerService
}
public async Task<Server> Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null,
Action<Server>? modifyDetails = null)
Action<Server>? modifyDetails = null, int allocations = 1)
{
var user = UserRepository
.Get()
@ -253,7 +260,10 @@ public class ServerService
if (n == null)
{
node = NodeRepository.Get().Include(x => x.Allocations).First(); //TODO: Smart deploy
node = NodeRepository
.Get()
.Include(x => x.Allocations)
.First(); //TODO: Add smart deploy maybe
}
else
{
@ -263,22 +273,27 @@ public class ServerService
.First(x => x.Id == n.Id);
}
NodeAllocation freeAllo;
NodeAllocation[] freeAllocations;
try
{
freeAllo = node.Allocations.First(a => !ServerRepository.Get()
.SelectMany(s => s.Allocations)
.Any(b => b.Id == a.Id)); // Thank you ChatGPT <3
freeAllocations = node.Allocations
.Where(a => !ServerRepository.Get()
.SelectMany(s => s.Allocations)
.Any(b => b.Id == a.Id))
.Take(allocations).ToArray();
}
catch (Exception)
{
throw new DisplayException("No allocation found");
}
if (freeAllo == null)
if (!freeAllocations.Any())
throw new DisplayException("No allocation found");
if (freeAllocations.Length != allocations)
throw new DisplayException("Not enough allocations found");
var server = new Server()
{
Cpu = cpu,
@ -289,11 +304,8 @@ public class ServerService
Owner = user,
Node = node,
Uuid = Guid.NewGuid(),
MainAllocation = freeAllo,
Allocations = new()
{
freeAllo
},
MainAllocation = freeAllocations.First(),
Allocations = freeAllocations.ToList(),
Backups = new(),
OverrideStartup = "",
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default)
@ -321,10 +333,7 @@ public class ServerService
StartOnCompletion = false
});
await AuditLogService.Log(AuditLogType.CreateServer, x =>
{
x.Add<Server>(newServerData.Uuid);
});
await AuditLogService.Log(AuditLogType.CreateServer, x => { x.Add<Server>(newServerData.Uuid); });
return newServerData;
}
@ -332,11 +341,11 @@ public class ServerService
{
await ErrorLogService.Log(e, x =>
{
x.Add<Server>(newServerData.Uuid);
x.Add<Server>(newServerData.Uuid);
x.Add<Node>(node.Id);
});
ServerRepository.Delete(newServerData);
ServerRepository.Delete(newServerData); //TODO Remove unsinged table stuff
throw new DisplayException("Error creating server on wings");
}
@ -348,10 +357,7 @@ public class ServerService
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null);
await AuditLogService.Log(AuditLogType.ReinstallServer, x =>
{
x.Add<Server>(server.Uuid);
});
await AuditLogService.Log(AuditLogType.ReinstallServer, x => { x.Add<Server>(server.Uuid); });
}
public async Task<Server> SftpServerLogin(int serverId, int id, string password)
@ -360,10 +366,7 @@ public class ServerService
if (server == null)
{
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x =>
{
x.Add<int>(id);
});
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => { x.Add<int>(id); });
throw new Exception("Server not found");
}
@ -389,10 +392,42 @@ public class ServerService
public async Task Delete(Server s)
{
throw new DisplayException("Deleting a server is currently a bit buggy. So its disabled for your safety");
var server = EnsureNodeData(s);
var backups = await GetBackups(server);
foreach (var backup in backups)
{
try
{
await DeleteBackup(server, backup);
}
catch (Exception)
{
// ignored
}
}
await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null);
//TODO: Fix empty data models
ServerRepository.Delete(s);
server.Allocations = new();
server.MainAllocation = null;
server.Variables = new();
server.Backups = new();
ServerRepository.Update(server);
ServerRepository.Delete(server);
}
public async Task<bool> IsHostUp(Server s)
{
var server = EnsureNodeData(s);
return await NodeService.IsHostUp(server.Node);
}
}

View file

@ -6,12 +6,18 @@ namespace Moonlight.App.Services;
public class SmartDeployService
{
private readonly NodeRepository NodeRepository;
private readonly PleskServerRepository PleskServerRepository;
private readonly WebsiteService WebsiteService;
private readonly NodeService NodeService;
public SmartDeployService(NodeRepository nodeRepository, NodeService nodeService)
public SmartDeployService(
NodeRepository nodeRepository,
NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService)
{
NodeRepository = nodeRepository;
NodeService = nodeService;
PleskServerRepository = pleskServerRepository;
WebsiteService = websiteService;
}
public async Task<Node?> GetNode()
@ -32,6 +38,21 @@ public class SmartDeployService
return data.MaxBy(x => x.Value).Key;
}
public async Task<PleskServer?> GetPleskServer()
{
var result = new List<PleskServer>();
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
{
if (await WebsiteService.IsHostUp(pleskServer))
{
result.Add(pleskServer);
}
}
return result.FirstOrDefault();
}
private async Task<double> GetUsageScore(Node node)
{
var score = 0;

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -0,0 +1,58 @@
using Logging.Net;
using Moonlight.App.Helpers;
namespace Moonlight.App.Services;
public class StorageService
{
public StorageService()
{
EnsureCreated();
}
public void EnsureCreated()
{
Directory.CreateDirectory(PathBuilder.Dir("storage", "uploads"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "configs"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "resources"));
if(IsEmpty(PathBuilder.Dir("storage", "resources")))
{
Logger.Info("Default resources not found. Copying default resources");
CopyFilesRecursively(
PathBuilder.Dir("defaultstorage", "resources"),
PathBuilder.Dir("storage", "resources")
);
}
if (IsEmpty(PathBuilder.Dir("storage", "configs")))
{
Logger.Info("Default configs not found. Copying default configs");
CopyFilesRecursively(
PathBuilder.Dir("defaultstorage", "configs"),
PathBuilder.Dir("storage", "configs")
);
}
}
private bool IsEmpty(string path)
{
return !Directory.EnumerateFileSystemEntries(path).Any();
}
private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
}
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories))
{
File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
}
}
}

View file

@ -18,6 +18,7 @@ public class UserService
private readonly AuditLogService AuditLogService;
private readonly MailService MailService;
private readonly IdentityService IdentityService;
private readonly IpLocateService IpLocateService;
private readonly string JwtSecret;
@ -28,7 +29,7 @@ public class UserService
SecurityLogService securityLogService,
AuditLogService auditLogService,
MailService mailService,
IdentityService identityService)
IdentityService identityService, IpLocateService ipLocateService)
{
UserRepository = userRepository;
TotpService = totpService;
@ -36,6 +37,7 @@ public class UserService
AuditLogService = auditLogService;
MailService = mailService;
IdentityService = identityService;
IpLocateService = ipLocateService;
JwtSecret = configService
.GetSection("Moonlight")
@ -77,6 +79,7 @@ public class UserService
});
await MailService.SendMail(user!, "register", values => {});
await AuditLogService.Log(AuditLogType.Register, x =>
{
x.Add<User>(user.Email);
@ -177,11 +180,13 @@ public class UserService
}
else
{
var location = await IpLocateService.GetLocation();
await MailService.SendMail(user!, "passwordChange", values =>
{
values.Add("Ip", IdentityService.GetIp());
values.Add("Device", IdentityService.GetDevice());
values.Add("Location", "In your walls");
values.Add("Location", location);
});
await AuditLogService.Log(AuditLogType.ChangePassword, x =>
@ -201,6 +206,7 @@ public class UserService
{
x.Add<int>(id);
});
throw new Exception("Invalid username");
}
@ -223,12 +229,17 @@ public class UserService
public async Task<string> GenerateToken(User user, bool sendMail = false)
{
await MailService.SendMail(user!, "login", values =>
var location = await IpLocateService.GetLocation();
if (sendMail)
{
values.Add("Ip", IdentityService.GetIp());
values.Add("Device", IdentityService.GetDevice());
values.Add("Location", "In your walls");
});
await MailService.SendMail(user!, "login", values =>
{
values.Add("Ip", IdentityService.GetIp());
values.Add("Device", IdentityService.GetDevice());
values.Add("Location", location);
});
}
var token = JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
@ -257,11 +268,13 @@ public class UserService
await AuditLogService.Log(AuditLogType.PasswordReset, x => {});
var location = await IpLocateService.GetLocation();
await MailService.SendMail(user, "passwordReset", values =>
{
values.Add("Ip", IdentityService.GetIp());
values.Add("Device", IdentityService.GetDevice());
values.Add("Location", "In your walls");
values.Add("Location", location);
values.Add("Password", newPassword);
});
}

View file

@ -0,0 +1,383 @@
using Logging.Net;
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Moonlight.App.Helpers;
using Moonlight.App.Helpers.Files;
using Moonlight.App.Models.Plesk.Requests;
using Moonlight.App.Models.Plesk.Resources;
using Moonlight.App.Repositories;
using FileAccess = Moonlight.App.Helpers.Files.FileAccess;
namespace Moonlight.App.Services;
public class WebsiteService
{
private readonly WebsiteRepository WebsiteRepository;
private readonly PleskServerRepository PleskServerRepository;
private readonly PleskApiHelper PleskApiHelper;
private readonly UserRepository UserRepository;
public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository, UserRepository userRepository)
{
WebsiteRepository = websiteRepository;
PleskApiHelper = pleskApiHelper;
PleskServerRepository = pleskServerRepository;
UserRepository = userRepository;
}
public async Task<Website> Create(string baseDomain, User owner, PleskServer? ps = null)
{
if (WebsiteRepository.Get().Any(x => x.BaseDomain == baseDomain))
throw new DisplayException("A website with this domain does already exist");
var pleskServer = ps ?? PleskServerRepository.Get().First();
var ftpLogin = baseDomain;
var ftpPassword = StringHelper.GenerateString(16);
var w = new Website()
{
PleskServer = pleskServer,
Owner = owner,
BaseDomain = baseDomain,
PleskId = 0,
FtpPassword = ftpPassword,
FtpLogin = ftpLogin
};
var website = WebsiteRepository.Add(w);
try
{
var id = await GetAdminAccount(pleskServer);
var result = await PleskApiHelper.Post<CreateResult>(pleskServer, "domains", new CreateDomain()
{
Description = $"moonlight website {website.Id}",
Name = baseDomain,
HostingType = "virtual",
Plan = new()
{
Name = "Unlimited"
},
HostingSettings = new()
{
FtpLogin = ftpLogin,
FtpPassword = ftpPassword
},
OwnerClient = new()
{
Id = id
}
});
website.PleskId = result.Id;
WebsiteRepository.Update(website);
}
catch (Exception e)
{
WebsiteRepository.Delete(website);
throw;
}
return website;
}
public async Task Delete(Website w)
{
var website = EnsureData(w);
await PleskApiHelper.Delete(website.PleskServer, $"domains/{w.PleskId}", null);
WebsiteRepository.Delete(website);
}
public async Task<bool> IsHostUp(PleskServer pleskServer)
{
try
{
var res = await PleskApiHelper.Get<ServerStatus>(pleskServer, "server");
if (res != null)
return true;
}
catch (Exception e)
{
// ignored
}
return false;
}
public async Task<bool> IsHostUp(Website w)
{
var website = EnsureData(w);
try
{
var res = await PleskApiHelper.Get<ServerStatus>(website.PleskServer, "server");
if (res != null)
return true;
}
catch (Exception)
{
// ignored
}
return false;
}
#region Get host
public async Task<string> GetHost(PleskServer pleskServer)
{
return (await PleskApiHelper.Get<ServerStatus>(pleskServer, "server")).Hostname;
}
public async Task<string> GetHost(Website w)
{
var website = EnsureData(w);
return await GetHost(website.PleskServer);
}
#endregion
private async Task<int> GetAdminAccount(PleskServer pleskServer)
{
var users = await PleskApiHelper.Get<Client[]>(pleskServer, "clients");
var user = users.FirstOrDefault(x => x.Type == "admin");
if (user == null)
throw new DisplayException("No admin account in plesk found");
return user.Id;
}
#region SSL
public async Task<string[]> GetSslCertificates(Website w)
{
var website = EnsureData(w);
var certs = new List<string>();
var data = await ExecuteCli(website.PleskServer, "certificate", p =>
{
p.Add("-l");
p.Add("-domain");
p.Add(w.BaseDomain);
});
string[] lines = data.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string line in lines)
{
if (line.Contains("Lets Encrypt"))
{
string[] parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if(parts.Length > 6)
certs.Add($"{parts[4]} {parts[5]} {parts[6]}");
}
else if (line.Contains("Listing of SSL/TLS certificates repository was successful"))
{
// This line indicates the end of the certificate listing, so we can stop parsing
break;
}
}
return certs.ToArray();
}
public async Task CreateSslCertificate(Website w)
{
var website = EnsureData(w);
await ExecuteCli(website.PleskServer, "extension", p =>
{
p.Add("--exec");
p.Add("letsencrypt");
p.Add("cli.php");
p.Add("-d");
p.Add(website.BaseDomain);
p.Add("-m");
p.Add(website.Owner.Email);
});
}
public async Task DeleteSslCertificate(Website w, string name)
{
var website = EnsureData(w);
try
{
await ExecuteCli(website.PleskServer, "site", p =>
{
p.Add("-u");
p.Add(website.BaseDomain);
p.Add("-ssl");
p.Add("false");
});
try
{
await ExecuteCli(website.PleskServer, "certificate", p =>
{
p.Add("--remove");
p.Add(name);
p.Add("-domain");
p.Add(website.BaseDomain);
});
}
catch (Exception e)
{
Logger.Warn("Error removing ssl certificate");
Logger.Warn(e);
throw new DisplayException("An unknown error occured while removing ssl certificate");
}
}
catch (DisplayException)
{
// Redirect all display exception to soft error handler
throw;
}
catch (Exception e)
{
Logger.Warn("Error disabling ssl certificate");
Logger.Warn(e);
throw new DisplayException("An unknown error occured while disabling ssl certificate");
}
}
#endregion
#region Databases
public async Task<Models.Plesk.Resources.Database[]> GetDatabases(Website w)
{
var website = EnsureData(w);
var dbs = await PleskApiHelper.Get<Models.Plesk.Resources.Database[]>(
website.PleskServer,
$"databases?domain={w.BaseDomain}"
);
return dbs;
}
public async Task CreateDatabase(Website w, string name, string password)
{
var website = EnsureData(w);
var server = await GetDefaultDatabaseServer(website);
if (server == null)
throw new DisplayException("No database server marked as default found");
var dbReq = new CreateDatabase()
{
Name = name,
Type = "mysql",
ParentDomain = new()
{
Name = website.BaseDomain
},
ServerId = server.Id
};
var db = await PleskApiHelper.Post<Models.Plesk.Resources.Database>(website.PleskServer, "databases", dbReq);
if (db == null)
throw new DisplayException("Unable to create database via api");
var dbUserReq = new CreateDatabaseUser()
{
DatabaseId = db.Id,
Login = name,
Password = password
};
await PleskApiHelper.Post(website.PleskServer, "dbusers", dbUserReq);
}
public async Task DeleteDatabase(Website w, Models.Plesk.Resources.Database database)
{
var website = EnsureData(w);
var dbUsers = await PleskApiHelper.Get<DatabaseUser[]>(
website.PleskServer,
$"dbusers?dbId={database.Id}"
);
foreach (var dbUser in dbUsers)
{
await PleskApiHelper.Delete(website.PleskServer, $"dbusers/{dbUser.Id}", null);
}
await PleskApiHelper.Delete(website.PleskServer, $"databases/{database.Id}", null);
}
public async Task<DatabaseServer?> GetDefaultDatabaseServer(PleskServer pleskServer)
{
var dbServers = await PleskApiHelper.Get<DatabaseServer[]>(pleskServer, "dbservers");
return dbServers.FirstOrDefault(x => x.IsDefault);
}
public async Task<DatabaseServer?> GetDefaultDatabaseServer(Website w)
{
var website = EnsureData(w);
return await GetDefaultDatabaseServer(website.PleskServer);
}
#endregion
public async Task<FileAccess> CreateFileAccess(Website w)
{
var website = EnsureData(w);
var host = await GetHost(website.PleskServer);
return new FtpFileAccess(host, 21, website.FtpLogin, website.FtpPassword);
}
private async Task<string> ExecuteCli(
PleskServer server,
string cli, Action<List<string>>? parameters = null,
Action<Dictionary<string, string>>? variables = null
)
{
var p = new List<string>();
var v = new Dictionary<string, string>();
parameters?.Invoke(p);
variables?.Invoke(v);
var req = new CliCall()
{
Env = v,
Params = p
};
var res = await PleskApiHelper.Post<CliResult>(server, $"cli/{cli}/call", req);
return res.Stdout;
}
private Website EnsureData(Website website)
{
if (website.PleskServer == null || website.Owner == null)
return WebsiteRepository
.Get()
.Include(x => x.PleskServer)
.Include(x => x.Owner)
.First(x => x.Id == website.Id);
return website;
}
}

View file

@ -19,4 +19,7 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
RUN mkdir -p /app/storage
RUN rm -r /app/storage/*
COPY "Moonlight/defaultstorage" "/app/defaultstorage"
ENTRYPOINT ["dotnet", "Moonlight.dll"]

View file

@ -9,20 +9,22 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="aaPanelSharp" Version="1.0.0" />
<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" />
<PackageReference Include="BlazorDownloadFile" Version="2.4.0.2" />
<PackageReference Include="Blazored.Typeahead" Version="4.7.0" />
<PackageReference Include="BlazorMonaco" Version="2.1.0" />
<PackageReference Include="BlazorTable" Version="1.17.0" />
<PackageReference Include="CloudFlare.Client" Version="6.1.4" />
<PackageReference Include="CurrieTechnologies.Razor.SweetAlert2" Version="5.4.0" />
<PackageReference Include="Discord.Net" Version="3.9.0" />
<PackageReference Include="FluentFTP" Version="46.0.2" />
<PackageReference Include="GravatarSharp.Core" Version="1.0.1.2" />
<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>
@ -60,6 +62,8 @@
<_ContentIncludedByDefault Remove="Shared\Components\Tables\Table.razor" />
<_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Add.razor" />
<_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Edit.razor" />
<_ContentIncludedByDefault Remove="Shared\Components\News\NewsEditor.razor" />
<_ContentIncludedByDefault Remove="Shared\News\Edit.razor" />
</ItemGroup>
<ItemGroup>

View file

@ -45,8 +45,7 @@
<link rel="stylesheet" type="text/css" href="/_content/XtermBlazor/XtermBlazor.css"/>
<link rel="stylesheet" type="text/css" href="/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.css"/>
<link rel="stylesheet" type="text/css" href="/_content/Blazor.ContextMenu/blazorContextMenu.min.css"/>
<link rel="stylesheet" type="text/css" href="/_content/Blazored.Typeahead/blazored-typeahead.css" />
<link href="/assets/plugins/global/plugins.bundle.css" rel="stylesheet" type="text/css"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
@ -87,13 +86,12 @@
<script src="/_content/BlazorInputFile/inputfile.js"></script>
<script src="/_content/CurrieTechnologies.Razor.SweetAlert2/sweetAlert2.min.js"></script>
<script src="/_content/Blazor.ContextMenu/blazorContextMenu.min.js"></script>
<script src="/_content/Blazored.Typeahead/blazored-typeahead.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<script src="http://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.min.js"></script>
<script src="http://cdn.jsdelivr.net/npm/xterm-addon-search@0.8.2/lib/xterm-addon-search.min.js"></script>
<script src="http://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.5.0/lib/xterm-addon-web-links.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-search@0.8.2/lib/xterm-addon-search.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.5.0/lib/xterm-addon-web-links.min.js"></script>
<script src="/assets/js/xtermAddons.js"></script>
@ -114,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>

View file

@ -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
@ -60,7 +61,12 @@ namespace Moonlight
builder.Services.AddScoped<NotificationRepository>();
builder.Services.AddScoped<DdosAttackRepository>();
builder.Services.AddScoped<SubscriptionRepository>();
builder.Services.AddScoped<PleskServerRepository>();
builder.Services.AddScoped<WebsiteRepository>();
builder.Services.AddScoped<LoadingMessageRepository>();
builder.Services.AddScoped<NewsEntryRepository>();
builder.Services.AddScoped<StatisticsRepository>();
builder.Services.AddScoped<AuditLogEntryRepository>();
builder.Services.AddScoped<ErrorLogEntryRepository>();
@ -68,6 +74,7 @@ namespace Moonlight
// Services
builder.Services.AddSingleton<ConfigService>();
builder.Services.AddSingleton<StorageService>();
builder.Services.AddScoped<CookieService>();
builder.Services.AddScoped<IdentityService>();
builder.Services.AddScoped<IpLocateService>();
@ -90,6 +97,8 @@ namespace Moonlight
builder.Services.AddScoped<NotificationClientService>();
builder.Services.AddScoped<ModalService>();
builder.Services.AddScoped<SmartDeployService>();
builder.Services.AddScoped<WebsiteService>();
builder.Services.AddScoped<StatisticsViewService>();
builder.Services.AddScoped<GoogleOAuth2Service>();
builder.Services.AddScoped<DiscordOAuth2Service>();
@ -121,9 +130,11 @@ namespace Moonlight
builder.Services.AddSingleton<PaperApiHelper>();
builder.Services.AddSingleton<HostSystemHelper>();
builder.Services.AddScoped<DaemonApiHelper>();
builder.Services.AddScoped<PleskApiHelper>();
// Background services
builder.Services.AddSingleton<DiscordBotService>();
builder.Services.AddSingleton<StatisticsCaptureService>();
// Third party services
builder.Services.AddBlazorTable();
@ -156,6 +167,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>();

13
Moonlight/README.md Normal file
View file

@ -0,0 +1,13 @@
### Some explanations
defaultstorage:
This directory is for the default assets of a moonlight instance, e.g. lang files, images etc
storage:
This directory is empty in fresh moonlight instances and will be populated with example config upon first run.
Before using moonlight this config file has to be modified.
Also resources are going to be copied from the default storage to this storage.
To access files in this storage we recommend to use the PathBuilder functions to ensure cross platform compatibility.
The storage directory should be mounted to a specific path when using docker container so when the container is replaced/rebuild your storage will not be modified

View file

@ -7,11 +7,17 @@
@using Moonlight.App.Services
@using Moonlight.App.Services.OAuth2
@using Moonlight.App.Models.Forms
@using Moonlight.App.Services.Interop
@using Moonlight.App.Services.Sessions
@inject SmartTranslateService SmartTranslateService
@inject GoogleOAuth2Service GoogleOAuth2Service
@inject NavigationManager NavigationManager
@inject DiscordOAuth2Service DiscordOAuth2Service
@inject AlertService AlertService
@inject UserService UserService
@inject CookieService CookieService
<div class="d-flex flex-center">
<div class="card rounded-3 w-md-550px">
@ -46,38 +52,44 @@
</div>
</div>
<div class="separator separator-content my-14">
<div class="separator separator-content my-10">
<span class="w-125px text-gray-500 fw-semibold fs-7">
<TL>Or with email</TL>
</span>
</div>
<SmartForm Model="UserRegisterModel" OnValidSubmit="CreateUser">
<div class="fv-row mb-4 fv-plugins-icon-container">
<InputText @bind-Value="UserRegisterModel.Email" placeholder="@(SmartTranslateService.Translate("Email"))" name="email" autocomplete="off" class="form-control bg-transparent" />
</div>
<div class="row">
<div class="col-lg-6 mb-4 fv-plugins-icon-container">
<InputText @bind-Value="UserRegisterModel.FirstName" type="text" placeholder="@(SmartTranslateService.Translate("Firstname"))" name="text" class="form-control bg-transparent" />
</div>
<div class="fv-row mb-8 fv-plugins-icon-container">
<input type="text" placeholder="@(SmartTranslateService.Translate("Firstname"))" name="text" class="form-control bg-transparent">
</div>
<div class="col-lg-6 mb-4 fv-plugins-icon-container">
<InputText @bind-Value="UserRegisterModel.LastName" type="text" placeholder="@(SmartTranslateService.Translate("Lastname"))" name="text"class="form-control bg-transparent" />
</div>
</div>
<div class="fv-row mb-8 fv-plugins-icon-container">
<input type="text" placeholder="@(SmartTranslateService.Translate("Lastname"))" name="text"class="form-control bg-transparent">
</div>
<div class="fv-row mb-8 fv-plugins-icon-container">
<input type="text" placeholder="@(SmartTranslateService.Translate("Email"))" name="email" autocomplete="off" class="form-control bg-transparent">
</div>
<div class="fv-row mb-3 fv-plugins-icon-container">
<input type="password" placeholder="@(SmartTranslateService.Translate("Password"))" name="password" autocomplete="off" class="form-control bg-transparent">
</div>
<div class="fv-row mb-5 fv-plugins-icon-container">
<input type="password" placeholder="@(SmartTranslateService.Translate("Repeat password"))" name="password" autocomplete="off" class="form-control bg-transparent">
</div>
<div class="d-grid mb-10">
<button class="btn btn-primary">
<TL>Sign-up</TL>
</button>
</div>
<div class="row">
<div class="col-lg-6 mb-4 fv-plugins-icon-container">
<InputText @bind-Value="UserRegisterModel.Password" type="password" placeholder="@(SmartTranslateService.Translate("Password"))" name="password" autocomplete="off" class="form-control bg-transparent" />
</div>
<div class="col-lg-6 mb-4 fv-plugins-icon-container">
<InputText @bind-Value="UserRegisterModel.ConfirmPassword" type="password" placeholder="@(SmartTranslateService.Translate("Repeat password"))" name="password" autocomplete="off" class="form-control bg-transparent" />
</div>
</div>
<div class="d-grid mb-6">
<button type="submit" class="btn btn-primary">
<TL>Sign-up</TL>
</button>
</div>
</SmartForm>
<div class="text-gray-500 text-center fw-semibold fs-6">
<TL>Already registered?</TL>
@ -93,6 +105,8 @@
@code
{
private UserRegisterModel UserRegisterModel = new();
private async Task DoGoogle()
{
var url = await GoogleOAuth2Service.GetUrl();
@ -104,4 +118,21 @@
var url = await DiscordOAuth2Service.GetUrl();
NavigationManager.NavigateTo(url, true);
}
private async Task CreateUser()
{
if (UserRegisterModel.ConfirmPassword != UserRegisterModel.Password)
{
await AlertService.Error(SmartTranslateService.Translate("Passwords need to match"));
return;
}
var token = await UserService.Register(UserRegisterModel.Email, UserRegisterModel.Password, UserRegisterModel.FirstName, UserRegisterModel.LastName);
await CookieService.SetValue("token", token, 10);
if (NavigationManager.Uri.EndsWith("register"))
NavigationManager.NavigateTo("/", true);
else
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
}

View file

@ -52,10 +52,7 @@ else
{
receivedExceptions.Add(exception);
var user = await IdentityService.Get();
var id = user == null ? -1 : user.Id;
Logger.Error($"[{id}] An unhanded exception occured:");
Logger.Error($"An unhanded exception occured:");
Logger.Error(exception);
await base.OnErrorAsync(exception);

View file

@ -7,13 +7,35 @@
@inject AlertService AlertService
@inject SmartTranslateService SmartTranslateService
@ChildContent
@if (Crashed)
{
<div class="card card-flush h-md-100">
<div class="card-body d-flex flex-column justify-content-between mt-9 bgi-no-repeat bgi-size-cover bgi-position-x-center pb-0">
<div class="mb-10">
<div class="fs-2hx fw-bold text-gray-800 text-center mb-13">
<span class="me-2">
<TL>Ooops. This page is crashed</TL>
</span>
</div>
<div class="text-center">
<TL>This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page</TL>
</div>
</div>
</div>
</div>
}
else
{
@ChildContent
}
@code
{
private bool Crashed = false;
protected override async Task OnErrorAsync(Exception exception)
{
Logger.Debug(exception);
Logger.Warn(exception);
if (exception is DisplayException displayException)
{
@ -36,9 +58,21 @@
wingsException.Message
);
}
else if (exception is PleskException pleskException)
{
await AlertService.Error(
SmartTranslateService.Translate("Error from plesk"),
pleskException.Message
);
}
else if (exception is NotImplementedException)
{
await AlertService.Error(SmartTranslateService.Translate("This function is not implemented"));
}
else
{
throw exception;
Crashed = true;
await InvokeAsync(StateHasChanged);
}
}
}

View file

@ -24,7 +24,7 @@
else
{
<div class="card mb-7">
<div class="card-header">
<div class="card-header border-0 my-2">
<div class="card-title">
<div class="d-flex flex-stack">
<FilePath Access="Access" OnPathChanged="OnComponentStateChanged" />
@ -59,7 +59,7 @@ else
else
{
<button type="button" @onclick="Launch" class="btn btn-light-primary me-3">
<span class="svg-icon svg-icon-muted svg-icon-2hx">
<span class="svg-icon svg-icon-muted svg-icon-2">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M5 16C3.3 16 2 14.7 2 13C2 11.3 3.3 10 5 10H5.1C5 9.7 5 9.3 5 9C5 6.2 7.2 4 10 4C11.9 4 13.5 5 14.3 6.5C14.8 6.2 15.4 6 16 6C17.7 6 19 7.3 19 9C19 9.4 18.9 9.7 18.8 10C18.9 10 18.9 10 19 10C20.7 10 22 11.3 22 13C22 14.7 20.7 16 19 16H5ZM8 13.6H16L12.7 10.3C12.3 9.89999 11.7 9.89999 11.3 10.3L8 13.6Z" fill="currentColor"/>
<path d="M11 13.6V19C11 19.6 11.4 20 12 20C12.6 20 13 19.6 13 19V13.6H11Z" fill="currentColor"/>
@ -86,7 +86,7 @@ else
</div>
</div>
<div class="card card-body">
<div class="card card-body ps-9">
<FileView @ref="View"
Access="Access"
ContextActions="Actions"
@ -166,8 +166,8 @@ else
{
var stream = await Access.DownloadStream(x);
await ToastService.Info(SmartTranslateService.Translate("Starting download"));
await FileService.AddBuffer(stream);
await FileService.DownloadBinaryBuffers(x.Name);
stream.Position = 0;
await FileService.DownloadFile(fileName: x.Name, stream: stream, contentType: "application/octet-stream");
}
catch (NotImplementedException)
{

View file

@ -1,529 +0,0 @@
@using Moonlight.App.Helpers
@using BlazorContextMenu
@using BlazorDownloadFile
@using Logging.Net
@using Moonlight.App.Models.Files
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@inject AlertService AlertService
@inject ToastService ToastService
@inject NavigationManager NavigationManager
@inject SmartTranslateService TranslationService
@inject IBlazorDownloadFileService FileService
<div class="card card-flush">
@if (Editing == null)
{
<div class="card-header pt-8">
<div class="card-title">
<div class="d-flex align-items-center position-relative my-1">
<span class="svg-icon svg-icon-1 position-absolute ms-6">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.5" x="17.0365" y="15.1223" width="8.15546" height="2" rx="1" transform="rotate(45 17.0365 15.1223)" fill="currentColor"></rect>
<path d="M11 19C6.55556 19 3 15.4444 3 11C3 6.55556 6.55556 3 11 3C15.4444 3 19 6.55556 19 11C19 15.4444 15.4444 19 11 19ZM11 5C7.53333 5 5 7.53333 5 11C5 14.4667 7.53333 17 11 17C14.4667 17 17 14.4667 17 11C17 7.53333 14.4667 5 11 5Z" fill="currentColor"></path>
</svg>
</span>
<input type="text" @bind="Search" @bind:event="oninput" class="form-control form-control-solid w-250px ps-15" placeholder="@(TranslationService.Translate("Search files and folders"))">
</div>
</div>
<div class="card-toolbar">
@if (SelectedFiles.Count == 0)
{
<div class="d-flex justify-content-end">
<button type="button" @onclick="Launch" class="btn btn-light-primary me-3">
<span class="svg-icon svg-icon-muted svg-icon-2hx">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M5 16C3.3 16 2 14.7 2 13C2 11.3 3.3 10 5 10H5.1C5 9.7 5 9.3 5 9C5 6.2 7.2 4 10 4C11.9 4 13.5 5 14.3 6.5C14.8 6.2 15.4 6 16 6C17.7 6 19 7.3 19 9C19 9.4 18.9 9.7 18.8 10C18.9 10 18.9 10 19 10C20.7 10 22 11.3 22 13C22 14.7 20.7 16 19 16H5ZM8 13.6H16L12.7 10.3C12.3 9.89999 11.7 9.89999 11.3 10.3L8 13.6Z" fill="currentColor"/>
<path d="M11 13.6V19C11 19.6 11.4 20 12 20C12.6 20 13 19.6 13 19V13.6H11Z" fill="currentColor"/>
</svg>
</span>
<TL>Launch WinSCP</TL>
</button>
<button type="button" @onclick="CreateFolder" class="btn btn-light-primary me-3">
<span class="svg-icon svg-icon-2">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M10 4H21C21.6 4 22 4.4 22 5V7H10V4Z" fill="currentColor"></path>
<path d="M10.4 3.60001L12 6H21C21.6 6 22 6.4 22 7V19C22 19.6 21.6 20 21 20H3C2.4 20 2 19.6 2 19V4C2 3.4 2.4 3 3 3H9.2C9.7 3 10.2 3.20001 10.4 3.60001ZM16 12H13V9C13 8.4 12.6 8 12 8C11.4 8 11 8.4 11 9V12H8C7.4 12 7 12.4 7 13C7 13.6 7.4 14 8 14H11V17C11 17.6 11.4 18 12 18C12.6 18 13 17.6 13 17V14H16C16.6 14 17 13.6 17 13C17 12.4 16.6 12 16 12Z" fill="currentColor"></path>
<path opacity="0.3" d="M11 14H8C7.4 14 7 13.6 7 13C7 12.4 7.4 12 8 12H11V14ZM16 12H13V14H16C16.6 14 17 13.6 17 13C17 12.4 16.6 12 16 12Z" fill="currentColor"></path>
</svg>
</span>
<TL>New folder</TL>
</button>
<InputFile OnChange="OnFileChanged" type="file" id="fileUpload" hidden="" multiple=""/>
<label for="fileUpload" class="btn btn-primary me-3 pt-5 @(Uploading ? "disabled" : "")">
@if (Uploading)
{
<span>@(Percent)%</span>
}
else
{
<span class="svg-icon svg-icon-2">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M10 4H21C21.6 4 22 4.4 22 5V7H10V4Z" fill="currentColor"></path>
<path d="M10.4 3.60001L12 6H21C21.6 6 22 6.4 22 7V19C22 19.6 21.6 20 21 20H3C2.4 20 2 19.6 2 19V4C2 3.4 2.4 3 3 3H9.20001C9.70001 3 10.2 3.20001 10.4 3.60001ZM16 11.6L12.7 8.29999C12.3 7.89999 11.7 7.89999 11.3 8.29999L8 11.6H11V17C11 17.6 11.4 18 12 18C12.6 18 13 17.6 13 17V11.6H16Z" fill="currentColor"></path>
<path opacity="0.3" d="M11 11.6V17C11 17.6 11.4 18 12 18C12.6 18 13 17.6 13 17V11.6H11Z" fill="currentColor"></path>
</svg>
</span>
<TL>Upload</TL>
}
</label>
</div>
}
else
{
<div class="d-flex justify-content-end align-items-center">
<div class="fw-bold me-5">
<span class="me-2">
@(SelectedFiles.Count)
</span>
<TL>Selected</TL>
</div>
<button type="button" class="btn btn-primary me-3">
<TL>Move deleted</TL>
</button>
<button type="button" class="btn btn-danger">
<TL>Delete selected</TL>
</button>
</div>
}
</div>
</div>
<div class="card-body">
<div class="d-flex flex-stack">
<div class="badge badge-lg badge-light-primary">
<div class="d-flex align-items-center flex-wrap">
@{
var vx = "/";
}
<a @onclick:preventDefault @onclick="() => SetPath(vx)" href="#">/</a>
<span class="svg-icon svg-icon-2x svg-icon-primary mx-1">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.6343 12.5657L8.45001 16.75C8.0358 17.1642 8.0358 17.8358 8.45001 18.25C8.86423 18.6642 9.5358 18.6642 9.95001 18.25L15.4929 12.7071C15.8834 12.3166 15.8834 11.6834 15.4929 11.2929L9.95001 5.75C9.5358 5.33579 8.86423 5.33579 8.45001 5.75C8.0358 6.16421 8.0358 6.83579 8.45001 7.25L12.6343 11.4343C12.9467 11.7467 12.9467 12.2533 12.6343 12.5657Z" fill="currentColor"></path>
</svg>
</span>
@{
var cp = "/";
var lp = "/";
var pathParts = CurrentPath.Replace("\\", "/").Split('/', StringSplitOptions.RemoveEmptyEntries);
foreach (var path in pathParts)
{
lp = cp;
<a @onclick:preventDefault @onclick="() => SetPath(lp)" href="#">@(path)</a>
<span class="svg-icon svg-icon-2x svg-icon-primary mx-1">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.6343 12.5657L8.45001 16.75C8.0358 17.1642 8.0358 17.8358 8.45001 18.25C8.86423 18.6642 9.5358 18.6642 9.95001 18.25L15.4929 12.7071C15.8834 12.3166 15.8834 11.6834 15.4929 11.2929L9.95001 5.75C9.5358 5.33579 8.86423 5.33579 8.45001 5.75C8.0358 6.16421 8.0358 6.83579 8.45001 7.25L12.6343 11.4343C12.9467 11.7467 12.9467 12.2533 12.6343 12.5657Z" fill="currentColor"></path>
</svg>
</span>
cp += path + "/";
}
}
</div>
</div>
</div>
<div class="dt-bootstrap4 no-footer">
@if (Loading)
{
<div class="mt-5 alert alert-info">
<span>
<TL>Loading</TL> <span class="spinner-grow align-middle ms-2"></span>
</span>
</div>
}
else
{
<div class="table-responsive">
<table class="table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer">
<thead>
<tr class="text-start text-gray-400 fw-bold fs-7 text-uppercase gs-0">
<th class="w-10px pe-2">
<div class="form-check form-check-sm form-check-custom form-check-solid me-3">
<input class="form-check-input" type="checkbox" @onchange="OnAllFileToggle">
</div>
</th>
<th class="min-w-250px">
<TL>File name</TL>
</th>
<th class="min-w-10px">
<TL>File size</TL>
</th>
<th class="min-w-125px">
<TL>Last modified</TL>
</th>
<th class="w-125px"></th>
</tr>
</thead>
<tbody class="fw-semibold text-gray-600">
@foreach (var obj in Objects.Where(x => x.Name.Contains(Search)))
{
<tr class="odd">
<td>
<div class="form-check form-check-sm form-check-custom form-check-solid">
<input class="form-check-input" type="checkbox" @onchange="(e) => OnFileToggle(e, obj)">
</div>
</td>
<td>
<div class="d-flex align-items-center">
@if (obj.IsFile)
{
<span class="svg-icon svg-icon-2x svg-icon-primary me-4">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M19 22H5C4.4 22 4 21.6 4 21V3C4 2.4 4.4 2 5 2H14L20 8V21C20 21.6 19.6 22 19 22Z" fill="currentColor"></path>
<path d="M15 8H20L14 2V7C14 7.6 14.4 8 15 8Z" fill="currentColor"></path>
</svg>
</span>
}
else
{
<span class="svg-icon svg-icon-2x svg-icon-primary me-4">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M10 4H21C21.6 4 22 4.4 22 5V7H10V4Z" fill="currentColor"></path>
<path d="M9.2 3H3C2.4 3 2 3.4 2 4V19C2 19.6 2.4 20 3 20H21C21.6 20 22 19.6 22 19V7C22 6.4 21.6 6 21 6H12L10.4 3.60001C10.2 3.20001 9.7 3 9.2 3Z" fill="currentColor"></path>
</svg>
</span>
}
@if (obj.IsFile)
{
<a href="#" @onclick:preventDefault @onclick="() => OpenFile(obj)" class="text-gray-800 text-hover-primary">@(obj.Name)</a>
}
else
{
<a href="#" @onclick:preventDefault @onclick="() => CdPath(obj.Name)" class="text-gray-800 text-hover-primary">@(obj.Name)</a>
}
</div>
</td>
<td>
@(Formatter.FormatSize(obj.Size))
</td>
<td>
@(obj.UpdatedAt.ToShortDateString()) @(obj.UpdatedAt.ToShortTimeString())
</td>
<td class="text-end">
<div class="d-flex justify-content-end">
<div class="ms-2">
<ContextMenuTrigger MenuId="triggerMenu" MouseButtonTrigger="MouseButtonTrigger.Both" Data="obj">
<button class="btn btn-sm btn-icon btn-light btn-active-light-primary me-2">
<span class="svg-icon svg-icon-5 m-0">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
<rect x="17" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
<rect x="3" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
</svg>
</span>
</button>
</ContextMenuTrigger>
</div>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
}
else
{
if (Loading)
{
<div class="mt-5 alert alert-info">
<span>
<TL>Loading</TL> <span class="spinner-grow align-middle ms-2"></span>
</span>
</div>
}
else
{
<FileEditor OnSubmit="SaveFile" OnCancel="CloseFile" InitialData="@(InitialEditorData)" Language="@(Language)"></FileEditor>
}
}
</div>
<ContextMenu Id="triggerMenu" CssClass="bg-secondary z-10">
<Item Id="rename" OnClick="OnContextMenuClick"><TL>Rename</TL></Item>
<Item Id="move" OnClick="OnContextMenuClick"><TL>Move</TL></Item>
<Item Id="archive" OnClick="OnContextMenuClick"><TL>Archive</TL></Item>
<Item Id="unarchive" OnClick="OnContextMenuClick"><TL>Unarchive</TL></Item>
<Item Id="download" OnClick="OnContextMenuClick"><TL>Download</TL></Item>
<Item Id="delete" OnClick="OnContextMenuClick"><TL>Delete</TL></Item>
</ContextMenu>
@code
{
[Parameter]
public IFileAccess FileAccess { get; set; }
// Data
private List<FileManagerObject> SelectedFiles { get; set; } = new();
private List<FileManagerObject> Objects { get; set; } = new();
private string CurrentPath = "";
// Search
private string SearchValue = "";
private string Search
{
get { return SearchValue; }
set
{
SearchValue = value;
InvokeAsync(StateHasChanged);
}
}
// States
private bool Loading = false;
// States - Editor
private FileManagerObject? Editing = null;
private string InitialEditorData = "";
private string Language = "plaintext";
// States - File Upload
private bool Uploading = false;
private int Percent = 0;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await RefreshActive();
}
}
private async Task RefreshActive()
{
Loading = true;
await InvokeAsync(StateHasChanged);
await Refresh(false);
Loading = false;
await InvokeAsync(StateHasChanged);
}
private async Task Refresh(bool rerender = true)
{
SelectedFiles.Clear();
Objects.Clear();
CurrentPath = await FileAccess.GetCurrentPath();
var data = await FileAccess.GetDirectoryContent();
Objects = data.ToList();
if (rerender)
await InvokeAsync(StateHasChanged);
}
private async Task CdPath(string path)
{
await FileAccess.ChangeDirectory(path);
await RefreshActive();
}
private async Task SetPath(string path)
{
await FileAccess.SetDirectory(path);
await RefreshActive();
}
private async Task OnContextMenuClick(ItemClickEventArgs e)
{
var data = e.Data as FileManagerObject;
if (data == null)
return;
switch (e.MenuItem.Id)
{
case "delete":
await FileAccess.Delete(data);
break;
case "download":
if (data.IsFile)
{
try
{
var stream = await FileAccess.GetDownloadStream(data);
await ToastService.Info(TranslationService.Translate("Starting download"));
await FileService.AddBuffer(stream);
await FileService.DownloadBinaryBuffers(data.Name);
}
catch (NotImplementedException)
{
try
{
var url = await FileAccess.GetDownloadUrl(data);
NavigationManager.NavigateTo(url, true);
await ToastService.Info(TranslationService.Translate("Starting download"));
}
catch (Exception exception)
{
await ToastService.Error(TranslationService.Translate("Error starting download"));
Logger.Error("Error downloading file");
Logger.Error(exception.Message);
}
}
catch (Exception exception)
{
await ToastService.Error(TranslationService.Translate("Error starting download"));
Logger.Error("Error downloading file stream");
Logger.Error(exception.Message);
}
}
break;
case "rename":
var newName = await AlertService.Text(TranslationService.Translate("Rename"), TranslationService.Translate("Enter a new name"), data.Name);
var path = await FileAccess.GetCurrentPath();
await FileAccess.Move(data, path + "/" + newName);
break;
}
await Refresh(false);
}
private async Task OnFileToggle(ChangeEventArgs obj, FileManagerObject o)
{
if ((bool)obj.Value)
{
if (SelectedFiles.Contains(o))
return;
SelectedFiles.Add(o);
await InvokeAsync(StateHasChanged);
}
else
{
if (!SelectedFiles.Contains(o))
return;
SelectedFiles.Remove(o);
await InvokeAsync(StateHasChanged);
}
}
private async Task OnAllFileToggle(ChangeEventArgs obj)
{
if ((bool)obj.Value)
{
foreach (var o in Objects)
{
if (SelectedFiles.Contains(o))
continue;
SelectedFiles.Add(o);
}
await InvokeAsync(StateHasChanged);
}
else
{
foreach (var o in Objects)
{
if (!SelectedFiles.Contains(o))
continue;
SelectedFiles.Remove(o);
}
await InvokeAsync(StateHasChanged);
}
}
private async Task CreateFolder()
{
var name = await AlertService.Text(TranslationService.Translate("Create a new folder"), TranslationService.Translate("Enter a name"), "");
if (string.IsNullOrEmpty(name))
return;
await FileAccess.CreateDirectory(name);
await Refresh();
}
private async void SaveFile(string data)
{
if (Editing == null)
return;
await FileAccess.WriteFile(Editing, data);
Editing = null;
await Refresh();
}
private async Task OpenFile(FileManagerObject o)
{
Editing = o;
Loading = true;
await InvokeAsync(StateHasChanged);
InitialEditorData = await FileAccess.ReadFile(Editing);
Language = MonacoTypeHelper.GetEditorType(Editing.Name);
Loading = false;
await InvokeAsync(StateHasChanged);
}
private async void CloseFile()
{
Editing = null;
await InvokeAsync(StateHasChanged);
}
private async Task Launch()
{
NavigationManager.NavigateTo(await FileAccess.GetLaunchUrl());
}
private async Task OnFileChanged(InputFileChangeEventArgs arg)
{
Uploading = true;
await InvokeAsync(StateHasChanged);
foreach (var browserFile in arg.GetMultipleFiles())
{
if (browserFile.Size < 1024 * 1024 * 100)
{
Percent = 0;
try
{
await FileAccess.UploadFile(
browserFile.Name,
browserFile.OpenReadStream(1024 * 1024 * 100),
async (i) =>
{
Percent = i;
Task.Run(() => { InvokeAsync(StateHasChanged); });
});
await Refresh();
}
catch (Exception e)
{
await ToastService.Error(TranslationService.Translate("An unknown error occured while uploading a file"));
Logger.Error("Error uploading file");
Logger.Error(e);
}
}
else
{
await ToastService.Error(TranslationService.Translate("The uploaded file should not be bigger than 100MB"));
}
}
Uploading = false;
await InvokeAsync(StateHasChanged);
await ToastService.Success(TranslationService.Translate("File upload complete"));
}
}

View file

@ -97,7 +97,7 @@
<td>@(Formatter.FormatSize(file.Size))</td>
<td class="text-end">
<div class="d-flex justify-content-end">
<div class="ms-2 me-7">
<div class="ms-2 me-6">
@if (ContextActions.Any())
{
<ContextMenuTrigger MenuId="triggerMenu" MouseButtonTrigger="MouseButtonTrigger.Both" Data="file">

View file

@ -6,14 +6,14 @@
@if (!Working)
{
<button class="btn btn-danger" @onclick="Do">
<i class="bx bx-trash"></i>
<button class="btn btn-danger p-4 @AdditionalCssClasses" @onclick="Do">
<i class="bx bx-trash p-0 px-2"></i>
</button>
}
else
{
<button class="btn btn-danger disabled" disabled="">
<span class="spinner-border spinner-border-sm align-middle me-2"></span>
<button class="btn btn-danger p-4 disabled @AdditionalCssClasses" disabled="">
<span class="spinner-border spinner-border-sm align-middle me-2 p-0 px-2"></span>
</button>
}
@ -27,6 +27,9 @@ else
[Parameter]
public bool Confirm { get; set; } = false;
[Parameter]
public string AdditionalCssClasses { get; set; } = "";
private async Task Do()
{
Working = true;
@ -35,12 +38,7 @@ else
{
if (Confirm)
{
var b = await AlertService.YesNo(
SmartTranslateService.Translate("Are you sure?"),
SmartTranslateService.Translate("Do you really want to delete it?"),
SmartTranslateService.Translate("Yes"),
SmartTranslateService.Translate("No")
);
var b = await AlertService.ConfirmMath();
if (b)
{

View file

@ -0,0 +1,93 @@
@typeparam T
@using Logging.Net
@inherits InputBase<T>
<div class="dropdown w-100">
<div class="input-group">
@if (CurrentValue == null)
{
<input class="form-control" type="text" @bind-value="SearchTerm" @bind-value:event="oninput" placeholder="Search...">
}
else
{
<input class="form-control" type="text" value="@(DisplayFunc(CurrentValue))">
<button class="btn btn-primary" @onclick="() => SelectItem(default(T)!)">
<i class="bx bx-md bx-x"></i>
</button>
}
</div>
@{
var anyItems = FilteredItems.Any();
}
<div class="dropdown-menu w-100 @(anyItems ? "show" : "")" style="max-height: 200px; overflow-y: auto;">
@if (anyItems)
{
foreach (var item in FilteredItems)
{
<button class="dropdown-item py-2" type="button" @onclick="(() => SelectItem(item))">@DisplayFunc(item)</button>
}
}
</div>
</div>
@code {
[Parameter]
public IEnumerable<T> Items { get; set; }
[Parameter]
public Func<T, string> DisplayFunc { get; set; }
[Parameter]
public Func<T, string> SearchProp { get; set; }
private string SearchTerm
{
get => searchTerm;
set
{
FilteredItems = Items.Where(i => SearchProp(i).Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList();
searchTerm = value;
}
}
private string searchTerm = "";
private List<T> FilteredItems = new();
private void SelectItem(T item)
{
CurrentValue = item;
SearchTerm = "";
FilteredItems.Clear();
}
protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage)
{
// Check if the value is null or empty
if (string.IsNullOrEmpty(value))
{
result = default(T)!;
validationErrorMessage = "Value cannot be null or empty";
return false;
}
// Try to find an item that matches the search term
var item = FilteredItems.FirstOrDefault(i => SearchProp(i).Equals(value, StringComparison.OrdinalIgnoreCase));
if (item != null)
{
result = item;
validationErrorMessage = null;
return true;
}
else
{
result = default(T)!;
validationErrorMessage = $"No item found for search term '{value}'";
return false;
}
}
}

View file

@ -11,10 +11,13 @@
@code
{
[Parameter]
public TField[] Items { get; set; }
public IEnumerable<TField> Items { get; set; }
[Parameter]
public Func<TField, string> DisplayField { get; set; }
[Parameter]
public Func<Task>? OnChange { get; set; }
protected override void OnInitialized()
{
@ -53,6 +56,7 @@
{
Value = i;
ValueChanged.InvokeAsync(i);
OnChange?.Invoke();
}
}
}

View file

@ -39,6 +39,11 @@
<TL>Discord bot</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 7 ? "active" : "")" href="/admin/system/news">
<TL>News</TL>
</a>
</li>
</ul>
</div>
</div>

View file

@ -0,0 +1,22 @@
<div class="card mb-5 mb-xl-10">
<div class="card-body pt-0 pb-0">
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/websites">
<TL>Websites</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/websites/servers">
<TL>Plesk servers</TL>
</a>
</li>
</ul>
</div>
</div>
@code
{
[Parameter]
public int Index { get; set; } = 0;
}

View file

@ -52,14 +52,6 @@ else
<span class="menu-title"><TL>Websites</TL></span>
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/databases">
<span class="menu-icon">
<i class="bx bx-data"></i>
</span>
<span class="menu-title"><TL>Databases</TL></span>
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/domains">
<span class="menu-icon">
@ -155,32 +147,13 @@ else
</div>
</div>
</div>
<div data-kt-menu-trigger="click" class="menu-item menu-accordion">
<span class="menu-link">
<div class="menu-item">
<a class="menu-link" href="/admin/websites">
<span class="menu-icon">
<i class="bx bx-cube"></i>
<i class="bx bx-globe"></i>
</span>
<span class="menu-title"><TL>aaPanel</TL></span>
<span class="menu-arrow"></span>
</span>
<div class="menu-sub menu-sub-accordion">
<div class="menu-item">
<a class="menu-link" href="/admin/aapanel/">
<span class="menu-bullet">
<span class="bullet bullet-dot"></span>
</span>
<span class="menu-title"><TL>Overview</TL></span>
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/aapanel/databases">
<span class="menu-bullet">
<span class="bullet bullet-dot"></span>
</span>
<span class="menu-title"><TL>Databases</TL></span>
</a>
</div>
</div>
<span class="menu-title"><TL>Websites</TL></span>
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/users">

View file

@ -2,13 +2,12 @@
@using Moonlight.App.Repositories.Servers
@using Moonlight.Shared.Components.FileManagerPartials
@using Moonlight.App.Database.Entities
@using Moonlight.App.Helpers
@using Moonlight.App.Helpers.Files
@using Moonlight.App.Services
@inject ServerRepository ServerRepository
@inject WingsApiHelper WingsApiHelper
@inject SmartTranslateService SmartTranslateService
@inject ServerService ServerService
<div class="col">
<div class="card card-body">
@ -42,14 +41,9 @@
private FileSelectModal FileSelectModal;
private LazyLoader LazyLoader;
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
Access = new WingsFileAccess(WingsApiHelper,
null!,
CurrentServer,
null!,
null!
);
Access = await ServerService.CreateFileAccess(CurrentServer, null!);
}
private async Task Load(LazyLoader lazyLoader)

Some files were not shown because too many files have changed in this diff Show more