Added base models for servers. Added ws packet connection utility. Added some ui from old branch. Added some packeges. And more smaller things
This commit is contained in:
parent
33c1ffa0ba
commit
6fd1336f1c
38 changed files with 2991 additions and 221 deletions
1026
Moonlight/Core/Database/Migrations/20240127110558_AddedServerModels.Designer.cs
generated
Normal file
1026
Moonlight/Core/Database/Migrations/20240127110558_AddedServerModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,266 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.Core.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedServerModels : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerImages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AllocationsNeeded = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
StartupCommand = table.Column<string>(type: "TEXT", nullable: false),
|
||||
StopCommand = table.Column<string>(type: "TEXT", nullable: false),
|
||||
OnlineDetection = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ParseConfigurations = table.Column<string>(type: "TEXT", nullable: false),
|
||||
InstallDockerImage = table.Column<string>(type: "TEXT", nullable: false),
|
||||
InstallShell = table.Column<string>(type: "TEXT", nullable: false),
|
||||
InstallScript = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Author = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DonateUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
UpdateUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
DefaultDockerImageIndex = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerImages", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerImageVariables",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Key = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DefaultValue = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DisplayName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AllowUserToEdit = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
AllowUserToView = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerImageVariables", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerNodes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Fqdn = table.Column<string>(type: "TEXT", nullable: false),
|
||||
UseSsl = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
Token = table.Column<string>(type: "TEXT", nullable: false),
|
||||
HttpPort = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
FtpPort = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerNodes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerDockerImages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DisplayName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AutoPull = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ServerImageId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerDockerImages", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerDockerImages_ServerImages_ServerImageId",
|
||||
column: x => x.ServerImageId,
|
||||
principalTable: "ServerImages",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerAllocations",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
IpAddress = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Port = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ServerId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
ServerNodeId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerAllocations", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerAllocations_ServerNodes_ServerNodeId",
|
||||
column: x => x.ServerNodeId,
|
||||
principalTable: "ServerNodes",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Servers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ServiceId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Cpu = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Memory = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Disk = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ImageId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
DockerImageIndex = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
OverrideStartupCommand = table.Column<string>(type: "TEXT", nullable: true),
|
||||
NodeId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MainAllocationId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Servers", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Servers_ServerAllocations_MainAllocationId",
|
||||
column: x => x.MainAllocationId,
|
||||
principalTable: "ServerAllocations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Servers_ServerImages_ImageId",
|
||||
column: x => x.ImageId,
|
||||
principalTable: "ServerImages",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Servers_ServerNodes_NodeId",
|
||||
column: x => x.NodeId,
|
||||
principalTable: "ServerNodes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Servers_Services_ServiceId",
|
||||
column: x => x.ServiceId,
|
||||
principalTable: "Services",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ServerVariables",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Key = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Value = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ServerId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ServerVariables", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ServerVariables_Servers_ServerId",
|
||||
column: x => x.ServerId,
|
||||
principalTable: "Servers",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerAllocations_ServerId",
|
||||
table: "ServerAllocations",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerAllocations_ServerNodeId",
|
||||
table: "ServerAllocations",
|
||||
column: "ServerNodeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerDockerImages_ServerImageId",
|
||||
table: "ServerDockerImages",
|
||||
column: "ServerImageId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Servers_ImageId",
|
||||
table: "Servers",
|
||||
column: "ImageId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Servers_MainAllocationId",
|
||||
table: "Servers",
|
||||
column: "MainAllocationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Servers_NodeId",
|
||||
table: "Servers",
|
||||
column: "NodeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Servers_ServiceId",
|
||||
table: "Servers",
|
||||
column: "ServiceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ServerVariables_ServerId",
|
||||
table: "ServerVariables",
|
||||
column: "ServerId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_ServerAllocations_Servers_ServerId",
|
||||
table: "ServerAllocations",
|
||||
column: "ServerId",
|
||||
principalTable: "Servers",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ServerAllocations_ServerNodes_ServerNodeId",
|
||||
table: "ServerAllocations");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Servers_ServerNodes_NodeId",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_ServerAllocations_Servers_ServerId",
|
||||
table: "ServerAllocations");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerDockerImages");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerImageVariables");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerVariables");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerNodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Servers");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerAllocations");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ServerImages");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Moonlight.Core.Database;
|
||||
using Moonlight.Core.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
|
@ -18,7 +17,52 @@ namespace Moonlight.Core.Database.Migrations
|
|||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("Balance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Flags")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Permissions")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TokenValidTimestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TotpKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -51,7 +95,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Posts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostComment", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostComment", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -82,7 +126,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("PostComments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostLike", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostLike", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -106,7 +150,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("PostLikes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.WordFilter", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.WordFilter", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -121,7 +165,313 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("WordFilters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Category", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Cpu")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Disk")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DockerImageIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ImageId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MainAllocationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Memory")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("NodeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("OverrideStartupCommand")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ServiceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImageId");
|
||||
|
||||
b.HasIndex("MainAllocationId");
|
||||
|
||||
b.HasIndex("NodeId");
|
||||
|
||||
b.HasIndex("ServiceId");
|
||||
|
||||
b.ToTable("Servers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ServerNodeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ServerNodeId");
|
||||
|
||||
b.ToTable("ServerAllocations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AutoPull")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ServerImageId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ServerImageId");
|
||||
|
||||
b.ToTable("ServerDockerImages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AllocationsNeeded")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DefaultDockerImageIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DonateUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallDockerImage")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallScript")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InstallShell")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OnlineDetection")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParseConfigurations")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StartupCommand")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StopCommand")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UpdateUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ServerImages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImageVariable", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AllowUserToEdit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AllowUserToView")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DefaultValue")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ServerImageVariables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Fqdn")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("FtpPort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HttpPort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Token")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("UseSsl")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ServerNodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("ServerVariables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConfigJsonOverride")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProductId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("RenewAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Suspended")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("Services");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.ServiceShare", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ServiceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ServiceId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("ServiceShares");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Category", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -144,7 +494,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Categories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Coupon", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Coupon", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -165,7 +515,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Coupons");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.CouponUse", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.CouponUse", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -186,7 +536,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("CouponUses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCode", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -207,7 +557,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("GiftCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCodeUse", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCodeUse", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -228,7 +578,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("GiftCodeUses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Product", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Product", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -278,64 +628,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Products");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConfigJsonOverride")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nickname")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProductId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("RenewAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Suspended")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("Services");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.ServiceShare", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ServiceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ServiceId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("ServiceShares");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Transaction", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Transaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -361,7 +654,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Transaction");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Theme", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Theming.Entities.Theme", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -393,7 +686,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Themes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -435,7 +728,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("Tickets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.TicketMessage", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
|
@ -469,52 +762,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.ToTable("TicketMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("Balance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Flags")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Permissions")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TokenValidTimestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TotpKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
||||
.WithMany()
|
||||
|
@ -525,7 +773,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.Navigation("Author");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostComment", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostComment", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
||||
.WithMany()
|
||||
|
@ -533,16 +781,16 @@ namespace Moonlight.Core.Database.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Community.Post", null)
|
||||
b.HasOne("Moonlight.Features.Community.Entities.Post", null)
|
||||
.WithMany("Comments")
|
||||
.HasForeignKey("PostId");
|
||||
|
||||
b.Navigation("Author");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostLike", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostLike", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Community.Post", null)
|
||||
b.HasOne("Moonlight.Features.Community.Entities.Post", null)
|
||||
.WithMany("Likes")
|
||||
.HasForeignKey("PostId");
|
||||
|
||||
|
@ -555,48 +803,67 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.CouponUse", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Coupon", "Coupon")
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", "Image")
|
||||
.WithMany()
|
||||
.HasForeignKey("CouponId")
|
||||
.HasForeignKey("ImageId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||
.WithMany("CouponUses")
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("Coupon");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCodeUse", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.GiftCode", "GiftCode")
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.ServerAllocation", "MainAllocation")
|
||||
.WithMany()
|
||||
.HasForeignKey("GiftCodeId")
|
||||
.HasForeignKey("MainAllocationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||
.WithMany("GiftCodeUses")
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("GiftCode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Product", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Category", "Category")
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", "Node")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.HasForeignKey("NodeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Category");
|
||||
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", "Service")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServiceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Image");
|
||||
|
||||
b.Navigation("MainAllocation");
|
||||
|
||||
b.Navigation("Node");
|
||||
|
||||
b.Navigation("Service");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.Server", null)
|
||||
.WithMany("Allocations")
|
||||
.HasForeignKey("ServerId");
|
||||
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", null)
|
||||
.WithMany("Allocations")
|
||||
.HasForeignKey("ServerNodeId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", null)
|
||||
.WithMany("DockerImages")
|
||||
.HasForeignKey("ServerImageId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.Servers.Entities.Server", null)
|
||||
.WithMany("Variables")
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Owner")
|
||||
.WithMany()
|
||||
|
@ -604,7 +871,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Product", "Product")
|
||||
b.HasOne("Moonlight.Features.StoreSystem.Entities.Product", "Product")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
|
@ -615,9 +882,9 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.ServiceShare", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.ServiceShare", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Service", null)
|
||||
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", null)
|
||||
.WithMany("Shares")
|
||||
.HasForeignKey("ServiceId");
|
||||
|
||||
|
@ -630,14 +897,55 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Transaction", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.CouponUse", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.StoreSystem.Entities.Coupon", "Coupon")
|
||||
.WithMany()
|
||||
.HasForeignKey("CouponId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||
.WithMany("CouponUses")
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("Coupon");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCodeUse", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.StoreSystem.Entities.GiftCode", "GiftCode")
|
||||
.WithMany()
|
||||
.HasForeignKey("GiftCodeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||
.WithMany("GiftCodeUses")
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("GiftCode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Product", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Features.StoreSystem.Entities.Category", "Category")
|
||||
.WithMany()
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Transaction", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Creator")
|
||||
.WithMany()
|
||||
|
@ -645,7 +953,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Service", "Service")
|
||||
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", "Service")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServiceId");
|
||||
|
||||
|
@ -654,36 +962,19 @@ namespace Moonlight.Core.Database.Migrations
|
|||
b.Navigation("Service");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.TicketMessage", b =>
|
||||
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Sender")
|
||||
.WithMany()
|
||||
.HasForeignKey("SenderId");
|
||||
|
||||
b.HasOne("Moonlight.Core.Database.Entities.Tickets.Ticket", null)
|
||||
b.HasOne("Moonlight.Features.Ticketing.Entities.Ticket", null)
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("TicketId");
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
||||
{
|
||||
b.Navigation("Comments");
|
||||
|
||||
b.Navigation("Likes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
||||
{
|
||||
b.Navigation("Shares");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
||||
{
|
||||
b.Navigation("CouponUses");
|
||||
|
@ -692,6 +983,40 @@ namespace Moonlight.Core.Database.Migrations
|
|||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||
{
|
||||
b.Navigation("Comments");
|
||||
|
||||
b.Navigation("Likes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||
{
|
||||
b.Navigation("Allocations");
|
||||
|
||||
b.Navigation("Variables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b =>
|
||||
{
|
||||
b.Navigation("DockerImages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b =>
|
||||
{
|
||||
b.Navigation("Allocations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||
{
|
||||
b.Navigation("Shares");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
|
117
Moonlight/Core/Helpers/HttpApiClient.cs
Normal file
117
Moonlight/Core/Helpers/HttpApiClient.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class HttpApiClient<TException> : IDisposable where TException : Exception
|
||||
{
|
||||
private readonly HttpClient Client;
|
||||
private readonly string BaseUrl;
|
||||
|
||||
public HttpApiClient(string baseUrl, string token)
|
||||
{
|
||||
Client = new();
|
||||
Client.DefaultRequestHeaders.Add("Authorization", token);
|
||||
|
||||
BaseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";
|
||||
}
|
||||
|
||||
public async Task<string> Send(HttpMethod method, string path, string? body = null,
|
||||
string contentType = "text/plain")
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
|
||||
request.RequestUri = new Uri(BaseUrl + path);
|
||||
request.Method = method;
|
||||
|
||||
if (body != null)
|
||||
request.Content = new StringContent(body, Encoding.UTF8, new MediaTypeHeaderValue(contentType));
|
||||
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
await HandleRequestError(response, path);
|
||||
return "";
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
private async Task HandleRequestError(HttpResponseMessage response, string path)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var message = $"[{path}] ({response.StatusCode}): {content}";
|
||||
var exception = Activator.CreateInstance(typeof(TException), message) as Exception;
|
||||
|
||||
throw exception!;
|
||||
}
|
||||
|
||||
#region GET
|
||||
|
||||
public async Task<string> GetAsString(string path) =>
|
||||
await Send(HttpMethod.Get, path);
|
||||
|
||||
public async Task<T> Get<T>(string path) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Get, path))!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region POST
|
||||
|
||||
public async Task<string> PostAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Post, path, body, contentType);
|
||||
|
||||
public async Task<T> Post<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Post, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Post(string path, object? body = null) => await Send(HttpMethod.Post, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUT
|
||||
|
||||
public async Task<string> PutAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Put, path, body, contentType);
|
||||
|
||||
public async Task<T> Put<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Put, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Put(string path, object? body = null) => await Send(HttpMethod.Put, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region PATCH
|
||||
|
||||
public async Task<string> PatchAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Patch, path, body, contentType);
|
||||
|
||||
public async Task<T> Patch<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Patch, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Patch(string path, object? body = null) => await Send(HttpMethod.Patch, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region DELETE
|
||||
|
||||
public async Task<string> DeleteAsString(string path) =>
|
||||
await Send(HttpMethod.Delete, path);
|
||||
|
||||
public async Task<T> Delete<T>(string path) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Delete, path))!;
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Client.Dispose();
|
||||
}
|
||||
}
|
|
@ -79,10 +79,35 @@ public class WsPacketConnection
|
|||
return typedPacketType.GetProperty("Data")!.GetValue(typedPacket);
|
||||
}
|
||||
|
||||
public async Task<T?> Receive<T>() where T : class
|
||||
{
|
||||
var o = await Receive();
|
||||
|
||||
if (o == null)
|
||||
return default;
|
||||
|
||||
return (T)o;
|
||||
}
|
||||
|
||||
public async Task Close()
|
||||
{
|
||||
if(WebSocket.State == WebSocketState.Open)
|
||||
await WebSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
|
||||
await WebSocket.CloseOutputAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task WaitForClose()
|
||||
{
|
||||
var source = new TaskCompletionSource();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (WebSocket.State == WebSocketState.Open)
|
||||
await Task.Delay(10);
|
||||
|
||||
source.SetResult();
|
||||
});
|
||||
|
||||
await source.Task;
|
||||
}
|
||||
|
||||
public class RawPacket
|
||||
|
|
|
@ -11,6 +11,7 @@ public enum Permission
|
|||
AdminTickets = 1004,
|
||||
AdminCommunity = 1030,
|
||||
AdminServices = 1050,
|
||||
AdminServers = 1060,
|
||||
AdminStore = 1900,
|
||||
AdminViewExceptions = 1999,
|
||||
AdminRoot = 2000
|
||||
|
|
18
Moonlight/Core/Services/Interop/ClipboardService.cs
Normal file
18
Moonlight/Core/Services/Interop/ClipboardService.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class ClipboardService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public ClipboardService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task Copy(string content)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.clipboard.copy", content);
|
||||
}
|
||||
}
|
155
Moonlight/Features/Servers/Actions/ServerActions.cs
Normal file
155
Moonlight/Features/Servers/Actions/ServerActions.cs
Normal file
|
@ -0,0 +1,155 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Models.Enums;
|
||||
using Moonlight.Features.Servers.Services;
|
||||
using Moonlight.Features.ServiceManagement.Entities;
|
||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Features.Servers.Actions;
|
||||
|
||||
public class ServerActions : ServiceActions
|
||||
{
|
||||
public override async Task Create(IServiceProvider provider, Service service)
|
||||
{
|
||||
// Load all dependencies from the di
|
||||
var serverRepo = provider.GetRequiredService<Repository<Server>>();
|
||||
var imageRepo = provider.GetRequiredService<Repository<ServerImage>>();
|
||||
var nodeRepo = provider.GetRequiredService<Repository<ServerNode>>();
|
||||
var allocationRepo = provider.GetRequiredService<Repository<ServerAllocation>>();
|
||||
var serverService = provider.GetRequiredService<ServerService>();
|
||||
|
||||
// Parse the configuration file
|
||||
var config =
|
||||
JsonConvert.DeserializeObject<ServerConfig>(service.ConfigJsonOverride ?? service.Product.ConfigJson)!;
|
||||
|
||||
// Load and validate image
|
||||
|
||||
var image = imageRepo
|
||||
.Get()
|
||||
.Include(x => x.DockerImages)
|
||||
.Include(x => x.Variables)
|
||||
.FirstOrDefault(x => x.Id == config.ImageId);
|
||||
|
||||
if (image == null)
|
||||
throw new DisplayException("An image with this is is not found");
|
||||
|
||||
// Load and validate node
|
||||
|
||||
ServerNode? node = null;
|
||||
|
||||
if (config.NodeId != 0)
|
||||
{
|
||||
node = nodeRepo
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Id == config.NodeId);
|
||||
}
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
//TODO: Implement auto deploy
|
||||
throw new DisplayException("Auto deploy has not been implemented yet. Please specify the node id in the product configuration");
|
||||
}
|
||||
|
||||
// Load and validate server allocations
|
||||
ServerAllocation[] allocations = Array.Empty<ServerAllocation>();
|
||||
|
||||
if (config.DedicatedIp)
|
||||
{
|
||||
throw new DisplayException("The dedicated ip mode has not been implemented yet. Please disable the dedicated ip option in the product configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
allocations = allocationRepo
|
||||
.Get()
|
||||
.FromSqlRaw(
|
||||
$"SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL AND ServerNodeId={node.Id} LIMIT {image.AllocationsNeeded}")
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
if (allocations.Length < 1 || allocations.Length < image.AllocationsNeeded)
|
||||
throw new DisplayException($"Not enough free allocations found on node '{node.Name}'");
|
||||
|
||||
// Build server db model
|
||||
|
||||
var server = new Server()
|
||||
{
|
||||
Service = service,
|
||||
Cpu = config.Cpu,
|
||||
Memory = config.Memory,
|
||||
Disk = config.Disk,
|
||||
Node = node,
|
||||
MainAllocation = allocations.First(),
|
||||
Image = image,
|
||||
OverrideStartupCommand = null,
|
||||
DockerImageIndex = image.DefaultDockerImageIndex
|
||||
};
|
||||
|
||||
// Add allocations
|
||||
foreach (var allocation in allocations)
|
||||
server.Allocations.Add(allocation);
|
||||
|
||||
// Add variables
|
||||
foreach (var variable in image.Variables)
|
||||
{
|
||||
server.Variables.Add(new()
|
||||
{
|
||||
Key = variable.Key,
|
||||
Value = variable.DefaultValue
|
||||
});
|
||||
}
|
||||
|
||||
await serverService.Sync(server);
|
||||
await serverService.SendPowerAction(server, PowerAction.Install);
|
||||
}
|
||||
|
||||
public override Task Update(IServiceProvider provider, Service service)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override async Task Delete(IServiceProvider provider, Service service)
|
||||
{
|
||||
// Load dependencies from di
|
||||
var serverRepo = provider.GetRequiredService<Repository<Server>>();
|
||||
var serverService = provider.GetRequiredService<ServerService>();
|
||||
var serverVariableRepo = provider.GetRequiredService<Repository<ServerVariable>>();
|
||||
|
||||
// Load server
|
||||
var server = serverRepo
|
||||
.Get()
|
||||
.Include(x => x.Variables)
|
||||
.Include(x => x.MainAllocation)
|
||||
.FirstOrDefault(x => x.Service.Id == service.Id);
|
||||
|
||||
// Check if server already has been deleted
|
||||
if (server == null)
|
||||
{
|
||||
Logger.Warn($"Server for service {service.Id} is missing when trying to delete the service. Maybe it already has been deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the node
|
||||
await serverService.SyncDelete(server);
|
||||
|
||||
// Clear and delete the variables
|
||||
var variables = server.Variables.ToArray();
|
||||
|
||||
server.Variables.Clear();
|
||||
|
||||
serverRepo.Update(server);
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var variable in variables)
|
||||
serverVariableRepo.Delete(variable);
|
||||
}
|
||||
catch (Exception) { /* ignored, as we dont want a operation to fail which just deletes some old data */ }
|
||||
|
||||
// Delete the model
|
||||
serverRepo.Delete(server);
|
||||
}
|
||||
}
|
26
Moonlight/Features/Servers/Actions/ServerConfig.cs
Normal file
26
Moonlight/Features/Servers/Actions/ServerConfig.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace Moonlight.Features.Servers.Actions;
|
||||
|
||||
public class ServerConfig
|
||||
{
|
||||
[Description("The amount of cpu cores for a server instance. 100% = 1 Core")]
|
||||
public int Cpu { get; set; } = 100;
|
||||
|
||||
[Description("The amount of memory in megabytes for a server instance")]
|
||||
public int Memory { get; set; } = 1024;
|
||||
|
||||
[Description("The amount of disk space in megabytes for a server instance")]
|
||||
public int Disk { get; set; } = 1024;
|
||||
|
||||
[Description("The id of the image to use for a server")]
|
||||
public int ImageId { get; set; } = 1;
|
||||
|
||||
[Description(
|
||||
"The id of the node to use for the server. If not set, moonlight will search automaticly for the best node to deploy on")]
|
||||
public int NodeId { get; set; } = 0;
|
||||
|
||||
[Description(
|
||||
"This options specifies if moonlight should give the server an allocation which ip has not been used by another server. So the server will has its own ip")]
|
||||
public bool DedicatedIp { get; set; } = false;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Features.Servers.UI.Layouts;
|
||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Features.Servers.Actions;
|
||||
|
||||
public class ServerServiceDefinition : ServiceDefinition
|
||||
{
|
||||
public override ServiceActions Actions => new ServerActions();
|
||||
public override Type ConfigType => typeof(ServerConfig);
|
||||
public override async Task BuildUserView(ServiceViewContext context)
|
||||
{
|
||||
context.Layout = ComponentHelper.FromType<UserLayout>();
|
||||
}
|
||||
|
||||
public override Task BuildAdminView(ServiceViewContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -6,8 +6,6 @@ public class Server
|
|||
{
|
||||
public int Id { get; set; }
|
||||
public Service Service { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Cpu { get; set; }
|
||||
public int Memory { get; set; }
|
||||
|
|
|
@ -20,5 +20,7 @@ public class ServerImage
|
|||
public string? UpdateUrl { get; set; }
|
||||
|
||||
public List<ServerImageVariable> Variables = new();
|
||||
|
||||
public int DefaultDockerImageIndex { get; set; } = 0;
|
||||
public List<ServerDockerImage> DockerImages { get; set; }
|
||||
}
|
|
@ -5,6 +5,8 @@ public class ServerNode
|
|||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Fqdn { get; set; }
|
||||
public bool UseSsl { get; set; }
|
||||
public string Token { get; set; }
|
||||
public int HttpPort { get; set; }
|
||||
public int FtpPort { get; set; }
|
||||
|
|
16
Moonlight/Features/Servers/Exceptions/NodeException.cs
Normal file
16
Moonlight/Features/Servers/Exceptions/NodeException.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Moonlight.Features.Servers.Exceptions;
|
||||
|
||||
public class NodeException : Exception
|
||||
{
|
||||
public NodeException()
|
||||
{
|
||||
}
|
||||
|
||||
public NodeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NodeException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -60,4 +60,15 @@ public static class ServerExtensions
|
|||
|
||||
return serverConfiguration;
|
||||
}
|
||||
|
||||
public static ServerInstallConfiguration ToServerInstallConfiguration(this Server server)
|
||||
{
|
||||
var installConfiguration = new ServerInstallConfiguration();
|
||||
|
||||
installConfiguration.DockerImage = server.Image.InstallDockerImage;
|
||||
installConfiguration.Script = server.Image.InstallScript;
|
||||
installConfiguration.Shell = server.Image.InstallShell;
|
||||
|
||||
return installConfiguration;
|
||||
}
|
||||
}
|
27
Moonlight/Features/Servers/Helpers/MetaCache.cs
Normal file
27
Moonlight/Features/Servers/Helpers/MetaCache.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace Moonlight.Features.Servers.Helpers;
|
||||
|
||||
public class MetaCache<T>
|
||||
{
|
||||
private readonly Dictionary<int, T> Cache = new();
|
||||
|
||||
public Task Update(int id, Action<T> metaAction)
|
||||
{
|
||||
lock (Cache)
|
||||
{
|
||||
T? meta = default;
|
||||
|
||||
if (Cache.ContainsKey(id))
|
||||
meta = Cache[id];
|
||||
|
||||
if (meta == null)
|
||||
{
|
||||
meta = Activator.CreateInstance<T>();
|
||||
Cache.Add(id, meta);
|
||||
}
|
||||
|
||||
metaAction.Invoke(meta);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||
using Moonlight.Features.Servers.Models.Packets;
|
||||
using Moonlight.Features.Servers.Services;
|
||||
|
||||
namespace Moonlight.Features.Servers.Http.Controllers;
|
||||
|
@ -11,10 +14,12 @@ namespace Moonlight.Features.Servers.Http.Controllers;
|
|||
public class NodeController : Controller
|
||||
{
|
||||
private readonly NodeService NodeService;
|
||||
private readonly ServerService ServerService;
|
||||
|
||||
public NodeController(NodeService nodeService)
|
||||
public NodeController(NodeService nodeService, ServerService serverService)
|
||||
{
|
||||
NodeService = nodeService;
|
||||
ServerService = serverService;
|
||||
}
|
||||
|
||||
[HttpPost("notify/start")]
|
||||
|
@ -23,7 +28,7 @@ public class NodeController : Controller
|
|||
// Load node from request context
|
||||
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||
|
||||
await NodeService.UpdateMeta(node, meta =>
|
||||
await NodeService.Meta.Update(node.Id, meta =>
|
||||
{
|
||||
meta.IsBooting = true;
|
||||
});
|
||||
|
@ -37,11 +42,47 @@ public class NodeController : Controller
|
|||
// Load node from request context
|
||||
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||
|
||||
await NodeService.UpdateMeta(node, meta =>
|
||||
await NodeService.Meta.Update(node.Id, meta =>
|
||||
{
|
||||
meta.IsBooting = true;
|
||||
meta.IsBooting = false;
|
||||
});
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("ws")]
|
||||
public async Task<ActionResult> Ws()
|
||||
{
|
||||
// Validate if it is even a websocket connection
|
||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return BadRequest("This endpoint is only available for websockets");
|
||||
|
||||
// Accept websocket connection
|
||||
var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
// Build connection wrapper
|
||||
var wsPacketConnection = new WsPacketConnection(websocket);
|
||||
|
||||
// Register packets
|
||||
await wsPacketConnection.RegisterPacket<ServerStateUpdate>("serverStateUpdate");
|
||||
await wsPacketConnection.RegisterPacket<ServerOutputMessage>("serverOutputMessage");
|
||||
|
||||
while (websocket.State == WebSocketState.Open)
|
||||
{
|
||||
var packet = await wsPacketConnection.Receive();
|
||||
|
||||
if (packet is ServerStateUpdate serverStateUpdate)
|
||||
{
|
||||
await ServerService.Meta.Update(serverStateUpdate.Id, meta =>
|
||||
{
|
||||
meta.State = serverStateUpdate.State;
|
||||
meta.LastChangeTimestamp = DateTime.UtcNow;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await wsPacketConnection.Close();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ public class ServersControllers : Controller
|
|||
public async Task<ActionResult> GetAllServersWs()
|
||||
{
|
||||
// Validate if it is even a websocket connection
|
||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return BadRequest("This endpoint is only available for websockets");
|
||||
|
||||
// Accept websocket connection
|
||||
|
@ -33,6 +33,7 @@ public class ServersControllers : Controller
|
|||
|
||||
// Build connection wrapper
|
||||
var wsPacketConnection = new WsPacketConnection(websocket);
|
||||
await wsPacketConnection.RegisterPacket<int>("amount");
|
||||
await wsPacketConnection.RegisterPacket<ServerConfiguration>("serverConfiguration");
|
||||
|
||||
// Read server data for the node
|
||||
|
@ -42,10 +43,9 @@ public class ServersControllers : Controller
|
|||
var servers = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Allocations)
|
||||
.Include(x => x.Variables)
|
||||
.Include(x => x.MainAllocation)
|
||||
.Include(x => x.Image)
|
||||
.ThenInclude(x => x.Variables)
|
||||
.Include(x => x.Image)
|
||||
.ThenInclude(x => x.DockerImages)
|
||||
.Where(x => x.Node.Id == node.Id)
|
||||
.ToArray();
|
||||
|
@ -54,14 +54,59 @@ public class ServersControllers : Controller
|
|||
var serverConfigurations = servers
|
||||
.Select(x => x.ToServerConfiguration())
|
||||
.ToArray();
|
||||
|
||||
// Send the amount of configs the node will receive
|
||||
await wsPacketConnection.Send(servers.Length);
|
||||
|
||||
// Send the server configurations
|
||||
foreach (var serverConfiguration in serverConfigurations)
|
||||
await wsPacketConnection.Send(serverConfiguration);
|
||||
|
||||
// Close the connection
|
||||
await wsPacketConnection.Close();
|
||||
|
||||
await wsPacketConnection.WaitForClose();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult<ServerConfiguration>> GetServerById(int id)
|
||||
{
|
||||
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||
|
||||
var server = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Allocations)
|
||||
.Include(x => x.MainAllocation)
|
||||
.Include(x => x.Image)
|
||||
.ThenInclude(x => x.Variables)
|
||||
.Include(x => x.Image)
|
||||
.ThenInclude(x => x.DockerImages)
|
||||
.Where(x => x.Node.Id == node.Id)
|
||||
.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (server == null)
|
||||
return NotFound();
|
||||
|
||||
var configuration = server.ToServerConfiguration();
|
||||
|
||||
return Ok(configuration);
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/install")]
|
||||
public async Task<ActionResult<ServerInstallConfiguration>> GetServerInstallById(int id)
|
||||
{
|
||||
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||
|
||||
var server = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Image)
|
||||
.Where(x => x.Node.Id == node.Id)
|
||||
.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (server == null)
|
||||
return NotFound();
|
||||
|
||||
var configuration = server.ToServerInstallConfiguration();
|
||||
|
||||
return Ok(configuration);
|
||||
}
|
||||
}
|
|
@ -8,12 +8,12 @@ namespace Moonlight.Features.Servers.Http.Middleware;
|
|||
public class NodeMiddleware
|
||||
{
|
||||
private RequestDelegate Next;
|
||||
private readonly Repository<ServerNode> NodeRepository;
|
||||
private readonly IServiceProvider ServiceProvider;
|
||||
|
||||
public NodeMiddleware(RequestDelegate next, Repository<ServerNode> nodeRepository)
|
||||
public NodeMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
|
||||
{
|
||||
Next = next;
|
||||
NodeRepository = nodeRepository;
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
|
@ -57,7 +57,7 @@ public class NodeMiddleware
|
|||
return;
|
||||
}
|
||||
|
||||
var token = context.Request.Headers["Authorization"];
|
||||
var token = context.Request.Headers["Authorization"].ToString();
|
||||
|
||||
// Check if header is null
|
||||
if (string.IsNullOrEmpty(token))
|
||||
|
@ -65,9 +65,12 @@ public class NodeMiddleware
|
|||
context.Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var nodeRepo = scope.ServiceProvider.GetRequiredService<Repository<ServerNode>>();
|
||||
|
||||
// Check if any node has the token specified by the request
|
||||
var node = NodeRepository
|
||||
var node = nodeRepo
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Token == token);
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
namespace Moonlight.Features.Servers.Models.Abstractions;
|
||||
|
||||
public class ServerInstallConfiguration
|
||||
{
|
||||
public string DockerImage { get; set; }
|
||||
public string Shell { get; set; }
|
||||
public string Script { get; set; }
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using Moonlight.Features.Servers.Models.Enums;
|
||||
|
||||
namespace Moonlight.Features.Servers.Models.Abstractions;
|
||||
|
||||
public class ServerMeta
|
||||
{
|
||||
public ServerState State { get; set; }
|
||||
public DateTime LastChangeTimestamp { get; set; }
|
||||
}
|
9
Moonlight/Features/Servers/Models/Enums/PowerAction.cs
Normal file
9
Moonlight/Features/Servers/Models/Enums/PowerAction.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Moonlight.Features.Servers.Models.Enums;
|
||||
|
||||
public enum PowerAction
|
||||
{
|
||||
Start,
|
||||
Stop,
|
||||
Kill,
|
||||
Install
|
||||
}
|
11
Moonlight/Features/Servers/Models/Enums/ServerState.cs
Normal file
11
Moonlight/Features/Servers/Models/Enums/ServerState.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Moonlight.Features.Servers.Models.Enums;
|
||||
|
||||
public enum ServerState
|
||||
{
|
||||
Offline,
|
||||
Starting,
|
||||
Online,
|
||||
Stopping,
|
||||
Installing,
|
||||
Join2Start
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Moonlight.Features.Servers.Models.Forms.Admin;
|
||||
|
||||
public class CreateNodeForm
|
||||
{
|
||||
[Required(ErrorMessage = "You need to specify a name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify a fqdn")]
|
||||
[Description("This needs to be the ip or domain of the node")]
|
||||
public string Fqdn { get; set; }
|
||||
|
||||
[Description("This enables ssl for the http conenctions to the node. Only enable this if you have the cert installed on the node")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[Description("This is the http(s) port used by the node to allow communication to the node from the panel")]
|
||||
public int HttpPort { get; set; } = 8080;
|
||||
|
||||
[Description("This is the ftp port the panel and the users use to access their servers filesystem")]
|
||||
public int FtpPort { get; set; } = 2021;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Moonlight.Features.Servers.Models.Forms.Admin;
|
||||
|
||||
public class UpdateNodeForm
|
||||
{
|
||||
[Required(ErrorMessage = "You need to specify a name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify a fqdn")]
|
||||
[Description("This needs to be the ip or domain of the node")]
|
||||
public string Fqdn { get; set; }
|
||||
|
||||
[Description("This enables ssl for the http conenctions to the node. Only enable this if you have the cert installed on the node")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[Description("This is the http(s) port used by the node to allow communication to the node from the panel")]
|
||||
public int HttpPort { get; set; } = 8080;
|
||||
|
||||
[Description("This is the ftp port the panel and the users use to access their servers filesystem")]
|
||||
public int FtpPort { get; set; } = 2021;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Moonlight.Features.Servers.Models.Packets;
|
||||
|
||||
public class ServerOutputMessage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using Moonlight.Features.Servers.Models.Enums;
|
||||
|
||||
namespace Moonlight.Features.Servers.Models.Packets;
|
||||
|
||||
public class ServerStateUpdate
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ServerState State { get; set; }
|
||||
}
|
|
@ -1,30 +1,9 @@
|
|||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Helpers;
|
||||
using Moonlight.Features.Servers.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Features.Servers.Services;
|
||||
|
||||
public class NodeService
|
||||
{
|
||||
private readonly Dictionary<int, NodeMeta> MetaCache = new();
|
||||
|
||||
public Task UpdateMeta(ServerNode node, Action<NodeMeta> metaAction)
|
||||
{
|
||||
lock (MetaCache)
|
||||
{
|
||||
NodeMeta? meta = null;
|
||||
|
||||
if (MetaCache.ContainsKey(node.Id))
|
||||
meta = MetaCache[node.Id];
|
||||
|
||||
if (meta == null)
|
||||
{
|
||||
meta = new();
|
||||
MetaCache.Add(node.Id, meta);
|
||||
}
|
||||
|
||||
metaAction.Invoke(meta);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public readonly MetaCache<NodeMeta> Meta = new();
|
||||
}
|
55
Moonlight/Features/Servers/Services/ServerService.cs
Normal file
55
Moonlight/Features/Servers/Services/ServerService.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Exceptions;
|
||||
using Moonlight.Features.Servers.Helpers;
|
||||
using Moonlight.Features.Servers.Models.Abstractions;
|
||||
using Moonlight.Features.Servers.Models.Enums;
|
||||
|
||||
namespace Moonlight.Features.Servers.Services;
|
||||
|
||||
public class ServerService
|
||||
{
|
||||
public readonly MetaCache<ServerMeta> Meta = new();
|
||||
|
||||
private readonly IServiceProvider ServiceProvider;
|
||||
|
||||
public ServerService(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public async Task Sync(Server server)
|
||||
{
|
||||
using var httpClient = CreateHttpClient(server);
|
||||
await httpClient.Post($"servers/{server.Id}/sync");
|
||||
}
|
||||
|
||||
public async Task SyncDelete(Server server)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task SendPowerAction(Server server, PowerAction powerAction)
|
||||
{
|
||||
using var httpClient = CreateHttpClient(server);
|
||||
await httpClient.Post($"servers/{server.Id}/power/{powerAction.ToString().ToLower()}");
|
||||
}
|
||||
|
||||
private HttpApiClient<NodeException> CreateHttpClient(Server server)
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var serverRepo = scope.ServiceProvider.GetRequiredService<Repository<Server>>();
|
||||
|
||||
var serverWithNode = serverRepo
|
||||
.Get()
|
||||
.Include(x => x.Node)
|
||||
.First(x => x.Id == server.Id);
|
||||
|
||||
var protocol = serverWithNode.Node.UseSsl ? "https" : "http";
|
||||
var remoteUrl = $"{protocol}://{serverWithNode.Node.Fqdn}/";
|
||||
|
||||
return new HttpApiClient<NodeException>(remoteUrl, serverWithNode.Node.Token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<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/servers">
|
||||
<i class="bx bx-sm bx-server me-2"></i> Nodes
|
||||
</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/servers/images">
|
||||
<i class="bx bx-sm bx-memory-card me-2"></i> Images
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public int Index { get; set; }
|
||||
}
|
85
Moonlight/Features/Servers/UI/Components/Terminal.razor
Normal file
85
Moonlight/Features/Servers/UI/Components/Terminal.razor
Normal file
|
@ -0,0 +1,85 @@
|
|||
@using XtermBlazor
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@inject ClipboardService ClipboardService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<Xterm @ref="Term" Options="Options" AddonIds="AddonIds" OnFirstRender="OnFirstRender" />
|
||||
|
||||
@code
|
||||
{
|
||||
private Xterm Term;
|
||||
|
||||
private readonly TerminalOptions Options = new()
|
||||
{
|
||||
CursorBlink = false,
|
||||
CursorWidth = 0,
|
||||
Theme =
|
||||
{
|
||||
Background = "#000000",
|
||||
CursorAccent = "#000000",
|
||||
Cursor = "#000000"
|
||||
},
|
||||
DisableStdin = true,
|
||||
FontFamily = "monospace"
|
||||
};
|
||||
|
||||
private readonly string[] AddonIds = new[]
|
||||
{
|
||||
"xterm-addon-fit",
|
||||
"xterm-addon-web-links",
|
||||
"xterm-addon-search"
|
||||
};
|
||||
|
||||
private bool HasBeenRendered = false;
|
||||
private readonly List<string> UnRenderedMessageCache = new();
|
||||
|
||||
public async Task WriteLine(string content)
|
||||
{
|
||||
if(HasBeenRendered)
|
||||
await Term.WriteLine(content);
|
||||
else
|
||||
{
|
||||
lock (UnRenderedMessageCache)
|
||||
UnRenderedMessageCache.Add(content);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFirstRender()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Term.InvokeAddonFunctionVoidAsync("xterm-addon-fit", "fit");
|
||||
|
||||
// This disables the key handling for xterm completely in order to allow Strg + C copying and other features
|
||||
Term.AttachCustomKeyEventHandler(key =>
|
||||
{
|
||||
if (key.CtrlKey && key.Code == "KeyC" && key.Type == "keydown")
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var content = await Term.GetSelection();
|
||||
await ClipboardService.Copy(content);
|
||||
await ToastService.Info("Copied console selection to clipboard");
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
catch (Exception){ /* Ignore all js errors as the addons are not that important to risk a crash of the ui */ }
|
||||
|
||||
string[] messagesToWrite;
|
||||
|
||||
lock (UnRenderedMessageCache)
|
||||
{
|
||||
messagesToWrite = UnRenderedMessageCache.ToArray();
|
||||
UnRenderedMessageCache.Clear();
|
||||
}
|
||||
|
||||
foreach (var message in messagesToWrite)
|
||||
await Term.WriteLine(message);
|
||||
|
||||
HasBeenRendered = true;
|
||||
}
|
||||
}
|
5
Moonlight/Features/Servers/UI/Layouts/AdminLayout.razor
Normal file
5
Moonlight/Features/Servers/UI/Layouts/AdminLayout.razor
Normal file
|
@ -0,0 +1,5 @@
|
|||
<h3>AdminLayout</h3>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
289
Moonlight/Features/Servers/UI/Layouts/UserLayout.razor
Normal file
289
Moonlight/Features/Servers/UI/Layouts/UserLayout.razor
Normal file
|
@ -0,0 +1,289 @@
|
|||
@using Moonlight.Features.ServiceManagement.Entities
|
||||
@using Moonlight.Features.ServiceManagement.Models.Abstractions
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.Services
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Features.Servers.Models.Abstractions
|
||||
@using Moonlight.Features.Servers.Models.Enums
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject ServerService ServerService
|
||||
@inject ToastService ToastService
|
||||
|
||||
@implements IDisposable
|
||||
|
||||
<LazyLoader Load="Load" ShowAsCard="true">
|
||||
<div class="card card-body pb-0 pt-5">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex flex-row">
|
||||
<div class="d-flex flex-column ms-3">
|
||||
<span class="fw-bold text-gray-900 fs-3">
|
||||
@(Service.Nickname ?? $"Service {Service.Id}")
|
||||
</span>
|
||||
<span class="text-gray-500 pt-2 fw-semibold fs-6">
|
||||
@(Server.Image.Name)
|
||||
</span>
|
||||
</div>
|
||||
<div class="vr mx-4"></div>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="text-gray-900 fs-4">
|
||||
@{
|
||||
var color = "secondary";
|
||||
|
||||
switch (Meta.State)
|
||||
{
|
||||
case ServerState.Stopping:
|
||||
color = "warning";
|
||||
break;
|
||||
|
||||
case ServerState.Starting:
|
||||
color = "warning";
|
||||
break;
|
||||
|
||||
case ServerState.Offline:
|
||||
color = "danger";
|
||||
break;
|
||||
|
||||
case ServerState.Online:
|
||||
color = "success";
|
||||
break;
|
||||
|
||||
case ServerState.Installing:
|
||||
color = "primary";
|
||||
break;
|
||||
|
||||
case ServerState.Join2Start:
|
||||
color = "info";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
<i class="bx bx-sm bxs-circle text-@(color) @(Meta.State != ServerState.Offline ? $"pulse pulse-{color}" : "") align-middle"></i>
|
||||
<span class="align-middle">
|
||||
@(Meta.State)
|
||||
<span class="text-muted">(@(Formatter.FormatUptime(DateTime.UtcNow - Meta.LastChangeTimestamp)))</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-gray-800 pt-3 fw-semibold fs-5 row">
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">@(Server.Node.Fqdn):@(Server.MainAllocation.Port)</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span>
|
||||
<i class="bx bx-sm bx-globe align-middle text-info"></i>
|
||||
<span class="align-middle">188.75.252.37:10324</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mt-2">
|
||||
@if (Meta.State == ServerState.Offline)
|
||||
{
|
||||
<WButton
|
||||
OnClick="Start"
|
||||
CssClasses="btn btn-light-success btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</WButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-light-success btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-play"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
@if (Meta.State == ServerState.Offline || Meta.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-warning btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Stop"
|
||||
CssClasses="btn btn-light-warning btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-power-off"></i>
|
||||
</WButton>
|
||||
}
|
||||
|
||||
@if (Meta.State == ServerState.Offline || Meta.State == ServerState.Installing)
|
||||
{
|
||||
<button class="btn btn-light-danger btn-icon me-1 my-1 disabled" disabled="">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton
|
||||
OnClick="Kill"
|
||||
CssClasses="btn btn-light-danger btn-icon me-1 my-1">
|
||||
<i class="bx bx-sm bx-bomb"></i>
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="ms-2 mt-5 nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-6 fw-bold text-nowrap flex-nowrap hide-scrollbar" style="overflow-x: auto; overflow-y: hidden">
|
||||
@{
|
||||
var routeWithSlash = "/" + (Route ?? "");
|
||||
}
|
||||
|
||||
@foreach (var uiPage in ViewContext.Pages)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-active-primary ms-0 me-10v @(routeWithSlash == uiPage.Route ? "active" : "")" href="/service/@($"{Service.Id}{uiPage.Route}")">
|
||||
<i class="@(uiPage.Icon) me-2"></i>
|
||||
@(uiPage.Name)
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
@if (IsInstalling)
|
||||
{
|
||||
<div class="card card-body bg-black p-3">
|
||||
<Terminal @ref="InstallTerminal"/>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<CascadingValue Value="Server">
|
||||
<CascadingValue Value="Service">
|
||||
<CascadingValue Value="Meta">
|
||||
<SmartRouter Route="@Route">
|
||||
@foreach (var uiPage in ViewContext.Pages)
|
||||
{
|
||||
<Route Path="@uiPage.Route">
|
||||
@uiPage.Component
|
||||
</Route>
|
||||
}
|
||||
</SmartRouter>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
}
|
||||
</div>
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public Service Service { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ServiceViewContext ViewContext { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Route { get; set; }
|
||||
|
||||
private Server Server;
|
||||
private ServerMeta Meta;
|
||||
private CancellationTokenSource BackgroundCancel = new();
|
||||
|
||||
private Terminal? InstallTerminal;
|
||||
private bool IsInstalling = false;
|
||||
|
||||
private async Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
await lazyLoader.SetText("Loading server information");
|
||||
|
||||
Server = ServerRepository
|
||||
.Get()
|
||||
.Include(x => x.Image)
|
||||
.Include(x => x.Node)
|
||||
.Include(x => x.Variables)
|
||||
.Include(x => x.Allocations)
|
||||
.Include(x => x.MainAllocation)
|
||||
.First(x => x.Service.Id == Service.Id);
|
||||
|
||||
/*
|
||||
|
||||
// Load meta and setup event handlers
|
||||
Meta = await ServerService.Meta.Get(Server);
|
||||
Meta.OnStateChanged += async Task () =>
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
// Change from offline to installing
|
||||
// This will trigger the initialisation of the install view
|
||||
if (Meta.State == ServerState.Installing && !IsInstalling)
|
||||
{
|
||||
IsInstalling = true;
|
||||
|
||||
// After this call, we should have access to the install terminal reference
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
Meta.OnConsoleMessage += OnInstallConsoleMessage;
|
||||
}
|
||||
// Change from installing to offline
|
||||
// This will trigger the destruction of the install view
|
||||
else if (Meta.State == ServerState.Offline && IsInstalling)
|
||||
{
|
||||
IsInstalling = false;
|
||||
|
||||
Meta.OnConsoleMessage -= OnInstallConsoleMessage;
|
||||
|
||||
// After this call, the install terminal will disappear
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await ToastService.Info("Server installation complete");
|
||||
}
|
||||
};
|
||||
|
||||
// Send console subscription and add auto resubscribe for it
|
||||
await ServerService.Console.Subscribe(Server);
|
||||
|
||||
// We need this to revalidate to the daemon that we are still interested
|
||||
// in the console logs. By default the expiration time is 15 minutes from last
|
||||
// subscription so every 10 minutes should ensure we are subscribed
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (!BackgroundCancel.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(10));
|
||||
await ServerService.Console.Subscribe(Server);
|
||||
}
|
||||
});
|
||||
|
||||
// In order to update the timer correctly, we are calling a re
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (!BackgroundCancel.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private async Task OnInstallConsoleMessage(string message)
|
||||
{
|
||||
if(InstallTerminal != null)
|
||||
await InstallTerminal.WriteLine(message);
|
||||
}
|
||||
|
||||
private async Task Start() => await ServerService.SendPowerAction(Server, PowerAction.Start);
|
||||
|
||||
private async Task Stop() => await ServerService.SendPowerAction(Server, PowerAction.Stop);
|
||||
|
||||
private async Task Kill() => await ServerService.SendPowerAction(Server, PowerAction.Kill);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BackgroundCancel.Cancel();
|
||||
}
|
||||
}
|
72
Moonlight/Features/Servers/UI/Views/Admin/Index.razor
Normal file
72
Moonlight/Features/Servers/UI/Views/Admin/Index.razor
Normal file
|
@ -0,0 +1,72 @@
|
|||
@page "/admin/servers"
|
||||
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Helpers
|
||||
@using BlazorTable
|
||||
|
||||
@attribute [RequirePermission(Permission.AdminServers)]
|
||||
|
||||
@inject Repository<ServerNode> NodeRepository
|
||||
|
||||
<AdminServersNavigation Index="0"/>
|
||||
|
||||
<AutoCrud TItem="ServerNode"
|
||||
TCreateForm="CreateNodeForm"
|
||||
TUpdateForm="UpdateNodeForm"
|
||||
Title="Manage nodes"
|
||||
Load="Load"
|
||||
ValidateAdd="ValidateAdd"
|
||||
ValidateDelete="ValidateDelete">
|
||||
<View>
|
||||
<Column TableItem="ServerNode" Field="@(x => x.Id)" Title="Id" />
|
||||
<Column TableItem="ServerNode" Field="@(x => x.Name)" Title="Name">
|
||||
<Template>
|
||||
<a href="/admin/servers/nodes/@(context.Id)">@(context.Name)</a>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="ServerNode" Field="@(x => x.Fqdn)" Title="Fqdn" />
|
||||
<Column TableItem="ServerNode" Field="@(x => x.Id)" Title="Status">
|
||||
<Template>
|
||||
<span class="text-success">Online</span>
|
||||
</Template>
|
||||
</Column>
|
||||
</View>
|
||||
</AutoCrud>
|
||||
|
||||
@code
|
||||
{
|
||||
private ServerNode[] Load(Repository<ServerNode> repository)
|
||||
{
|
||||
return repository.Get().ToArray();
|
||||
}
|
||||
|
||||
private Task ValidateAdd(ServerNode node)
|
||||
{
|
||||
// Generate token
|
||||
node.Token = Formatter.GenerateString(32);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task ValidateDelete(ServerNode n)
|
||||
{
|
||||
var nodeHasAllocations = NodeRepository
|
||||
.Get()
|
||||
.Include(x => x.Allocations)
|
||||
.First(x => x.Id == n.Id)
|
||||
.Allocations
|
||||
.Any();
|
||||
|
||||
if (nodeHasAllocations)
|
||||
throw new DisplayException("The node still has allocations. Delete them in order to delete the node");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
@page "/admin/servers/nodes/{Id:int}"
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
}
|
|
@ -34,13 +34,8 @@
|
|||
<Folder Include="Features\Dummy\UI\Components\" />
|
||||
<Folder Include="Features\Dummy\UI\Views\" />
|
||||
<Folder Include="Features\Servers\Configuration\" />
|
||||
<Folder Include="Features\Servers\Exceptions\" />
|
||||
<Folder Include="Features\Servers\Helpers\" />
|
||||
<Folder Include="Features\Servers\Http\Requests\" />
|
||||
<Folder Include="Features\Servers\Http\Resources\" />
|
||||
<Folder Include="Features\Servers\Models\Forms\" />
|
||||
<Folder Include="Features\Servers\UI\Components\" />
|
||||
<Folder Include="Features\Servers\UI\Views\" />
|
||||
<Folder Include="Features\StoreSystem\Helpers\" />
|
||||
<Folder Include="Features\Ticketing\Models\Abstractions\" />
|
||||
</ItemGroup>
|
||||
|
@ -68,6 +63,7 @@
|
|||
<PackageReference Include="Serilog" Version="3.1.0-dev-02078" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0-dev-00923" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00972" />
|
||||
<PackageReference Include="XtermBlazor" Version="1.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using BlazorTable;
|
||||
using Microsoft.AspNetCore.WebSockets;
|
||||
using Moonlight.Core.Database;
|
||||
using Moonlight.Core.Actions.Dummy;
|
||||
using Moonlight.Core.Database;
|
||||
|
@ -13,6 +14,7 @@ using Moonlight.Core.Services.Users;
|
|||
using Moonlight.Core.Services.Utils;
|
||||
using Moonlight.Features.Advertisement.Services;
|
||||
using Moonlight.Features.Community.Services;
|
||||
using Moonlight.Features.Servers.Actions;
|
||||
using Moonlight.Features.Servers.Http.Middleware;
|
||||
using Moonlight.Features.Servers.Services;
|
||||
using Moonlight.Features.ServiceManagement.Entities.Enums;
|
||||
|
@ -73,6 +75,7 @@ builder.Services.AddScoped<ModalService>();
|
|||
builder.Services.AddScoped<AlertService>();
|
||||
builder.Services.AddScoped<FileDownloadService>();
|
||||
builder.Services.AddScoped<AdBlockService>();
|
||||
builder.Services.AddScoped<ClipboardService>();
|
||||
|
||||
// Services / Store
|
||||
builder.Services.AddScoped<StoreService>();
|
||||
|
@ -107,6 +110,7 @@ builder.Services.AddScoped<TicketCreateService>();
|
|||
|
||||
// Services / Servers
|
||||
builder.Services.AddSingleton<NodeService>();
|
||||
builder.Services.AddSingleton<ServerService>();
|
||||
|
||||
// Services
|
||||
builder.Services.AddScoped<IdentityService>();
|
||||
|
@ -135,6 +139,7 @@ var app = builder.Build();
|
|||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseWebSockets();
|
||||
|
||||
app.UseMiddleware<NodeMiddleware>();
|
||||
|
||||
|
@ -150,7 +155,7 @@ moonlightService.Application = app;
|
|||
moonlightService.LogPath = logPath;
|
||||
|
||||
var serviceService = app.Services.GetRequiredService<ServiceDefinitionService>();
|
||||
serviceService.Register<DummyServiceDefinition>(ServiceType.Server);
|
||||
serviceService.Register<ServerServiceDefinition>(ServiceType.Server);
|
||||
|
||||
await pluginService.RunPrePost(app);
|
||||
|
||||
|
|
33
Moonlight/wwwroot/js/moonlight.js
vendored
33
Moonlight/wwwroot/js/moonlight.js
vendored
|
@ -169,5 +169,38 @@ window.moonlight = {
|
|||
let editor = document.getElementById(id).ckeditorInstance;
|
||||
editor.setData(data);
|
||||
}
|
||||
},
|
||||
clipboard: {
|
||||
copy: function (text) {
|
||||
if (!navigator.clipboard) {
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.position = "fixed";
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
var msg = successful ? 'successful' : 'unsuccessful';
|
||||
} catch (err) {
|
||||
console.error('Fallback: Oops, unable to copy', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
},
|
||||
function (err) {
|
||||
console.error('Async: Could not copy text: ', err);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue