Recreated project with project template

This commit is contained in:
2024-12-05 15:35:23 +01:00
parent 7659403dc8
commit 3392407890
76 changed files with 2020 additions and 2922 deletions

10
.gitignore vendored
View File

@@ -421,5 +421,11 @@ FodyWeavers.xsd
.idea/**/libraries
# Moonlight
MoonlightServers/ApiServer/storage/**
/.idea/.idea.MoonlightServers/.idea
storage/
.idea/**/.idea
MoonlightServers.min.css
core.min.css
# Build script for nuget packages
finalPackages/
nupkgs/

View File

@@ -1,12 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class Allocation
{
public int Id { get; set; }
public string IpAddress { get; set; } = "0.0.0.0";
public int Port { get; set; }
public Server? Server { get; set; }
public Node Node { get; set; }
}

View File

@@ -1,18 +0,0 @@
using MoonlightServers.Shared.Enums;
namespace MoonlightServers.ApiServer.Database.Entities;
public class Backup
{
public int Id { get; set; }
public string Name { get; set; }
public BackupState State { get; set; }
public long Size { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime FinishedAt { get; set; } = DateTime.UtcNow;
public Server Server { get; set; }
}

View File

@@ -1,11 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class Network
{
public int Id { get; set; }
public string Name { get; set; }
public Node Node { get; set; }
public List<Server> Servers { get; set; } = new();
}

View File

@@ -1,16 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class Node
{
public int Id { get; set; }
public string Name { get; set; }
public string Fqdn { get; set; }
public int ApiPort { get; set; }
public string Token { get; set; }
public bool SslEnabled { get; set; }
public List<Allocation> Allocations { get; set; } = new();
public List<Server> Servers { get; set; } = new();
}

View File

@@ -1,26 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class Server
{
public int Id { get; set; }
public string Name { get; set; }
public int Cpu { get; set; }
public int Memory { get; set; }
public int Disk { get; set; }
public string? OverrideStartupCommand { get; set; } = null;
public string? OverrideDockerImage { get; set; } = null;
public bool VirtualDiskEnabled { get; set; } = false;
public Star Star { get; set; }
public int DockerImageIndex { get; set; } = 0;
public Node Node { get; set; }
public Network? Network { get; set; }
public List<Allocation> Allocations { get; set; } = new();
public List<ServerVariable> Variables { get; set; } = new();
public List<Backup> Backups { get; set; } = new();
}

View File

@@ -1,11 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class ServerVariable
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public Server Server { get; set; }
}

View File

@@ -1,31 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class Star
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string? DonationUrl { get; set; }
public string? UpdateUrl { get; set; }
public string StartupCommand { get; set; }
public string StopCommand { get; set; }
public string OnlineDetection { get; set; }
public string InstallShell { get; set; }
public string InstallDockerImage { get; set; }
public string InstallScript { get; set; }
public int RequiredAllocations { get; set; }
public string ParseConfiguration { get; set; }
public int DefaultDockerImageIndex { get; set; }
public bool AllowDockerImageChanging { get; set; }
public List<StarVariable> Variables { get; set; } = new();
public List<StarDockerImage> DockerImages { get; set; } = new();
public StarFolder? Folder { get; set; }
}

View File

@@ -1,11 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class StarDockerImage
{
public int Id { get; set; }
public string Identifier { get; set; }
public string DisplayName { get; set; }
public bool AutoPulling { get; set; } = true;
}

View File

@@ -1,10 +0,0 @@
namespace MoonlightServers.ApiServer.Database.Entities;
public class StarFolder
{
public int Id { get; set; }
public string Name { get; set; }
public List<Star> Stars { get; set; } = new();
}

View File

@@ -1,20 +0,0 @@
using MoonlightServers.Shared.Enums;
namespace MoonlightServers.ApiServer.Database.Entities;
public class StarVariable
{
public int Id { get; set; }
public string Key { get; set; }
public string DefaultValue { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool AllowViewing { get; set; } = false;
public bool AllowEditing { get; set; } = false;
public string? Filter { get; set; }
public StarVariableType Type { get; set; } = StarVariableType.Text;
}

View File

@@ -1,519 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MoonlightServers.ApiServer.Database;
#nullable disable
namespace MoonlightServers.ApiServer.Database.Migrations
{
[DbContext(typeof(ServersContext))]
[Migration("20240830074359_AddedServerEntities")]
partial class AddedServerEntities
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Servers")
.HasAnnotation("ProductVersion", "8.0.7")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("IpAddress")
.IsRequired()
.HasColumnType("longtext");
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("Allocations", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Backup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("FinishedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("ServerId")
.HasColumnType("int");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<int>("State")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ServerId");
b.ToTable("Backups", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("NodeId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("NodeId");
b.ToTable("Networks", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("ApiPort")
.HasColumnType("int");
b.Property<string>("Fqdn")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("SslEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Nodes", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Cpu")
.HasColumnType("int");
b.Property<int>("Disk")
.HasColumnType("int");
b.Property<int>("DockerImageIndex")
.HasColumnType("int");
b.Property<int>("Memory")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("NetworkId")
.HasColumnType("int");
b.Property<int>("NodeId")
.HasColumnType("int");
b.Property<string>("OverrideDockerImage")
.HasColumnType("longtext");
b.Property<string>("OverrideStartupCommand")
.HasColumnType("longtext");
b.Property<int>("StarId")
.HasColumnType("int");
b.Property<bool>("VirtualDiskEnabled")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("NetworkId");
b.HasIndex("NodeId");
b.HasIndex("StarId");
b.ToTable("Servers", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
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", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AllowDockerImageChanging")
.HasColumnType("tinyint(1)");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("DefaultDockerImageIndex")
.HasColumnType("int");
b.Property<string>("DonationUrl")
.HasColumnType("longtext");
b.Property<int?>("FolderId")
.HasColumnType("int");
b.Property<string>("InstallDockerImage")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallScript")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallShell")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("OnlineDetection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("ParseConfiguration")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("RequiredAllocations")
.HasColumnType("int");
b.Property<string>("StartupCommand")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("StopCommand")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("UpdateUrl")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("FolderId");
b.ToTable("Stars", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AutoPulling")
.HasColumnType("tinyint(1)");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Identifier")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("StarId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("StarId");
b.ToTable("StarDockerImages", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarFolder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("StarFolders", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AllowEditing")
.HasColumnType("tinyint(1)");
b.Property<bool>("AllowViewing")
.HasColumnType("tinyint(1)");
b.Property<string>("DefaultValue")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Filter")
.HasColumnType("longtext");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("StarId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("StarId");
b.ToTable("StarVariables", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany("Allocations")
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Allocations")
.HasForeignKey("ServerId");
b.Navigation("Node");
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Backup", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Backups")
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany()
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Node");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Network", "Network")
.WithMany("Servers")
.HasForeignKey("NetworkId");
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany("Servers")
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star")
.WithMany()
.HasForeignKey("StarId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Network");
b.Navigation("Node");
b.Navigation("Star");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Variables")
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.StarFolder", "Folder")
.WithMany("Stars")
.HasForeignKey("FolderId");
b.Navigation("Folder");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", null)
.WithMany("DockerImages")
.HasForeignKey("StarId");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", null)
.WithMany("Variables")
.HasForeignKey("StarId");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.Navigation("Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
{
b.Navigation("Allocations");
b.Navigation("Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.Navigation("Allocations");
b.Navigation("Backups");
b.Navigation("Variables");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.Navigation("DockerImages");
b.Navigation("Variables");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarFolder", b =>
{
b.Navigation("Stars");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,434 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MoonlightServers.ApiServer.Database.Migrations
{
/// <inheritdoc />
public partial class AddedServerEntities : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "Servers");
migrationBuilder.AlterDatabase()
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Nodes",
schema: "Servers",
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"),
Fqdn = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ApiPort = table.Column<int>(type: "int", nullable: false),
Token = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SslEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Nodes", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "StarFolders",
schema: "Servers",
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_StarFolders", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Networks",
schema: "Servers",
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"),
NodeId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Networks", x => x.Id);
table.ForeignKey(
name: "FK_Networks_Nodes_NodeId",
column: x => x.NodeId,
principalSchema: "Servers",
principalTable: "Nodes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Stars",
schema: "Servers",
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"),
Author = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DonationUrl = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
UpdateUrl = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
StartupCommand = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
StopCommand = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
OnlineDetection = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
InstallShell = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
InstallDockerImage = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
InstallScript = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
RequiredAllocations = table.Column<int>(type: "int", nullable: false),
ParseConfiguration = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DefaultDockerImageIndex = table.Column<int>(type: "int", nullable: false),
AllowDockerImageChanging = table.Column<bool>(type: "tinyint(1)", nullable: false),
FolderId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Stars", x => x.Id);
table.ForeignKey(
name: "FK_Stars_StarFolders_FolderId",
column: x => x.FolderId,
principalSchema: "Servers",
principalTable: "StarFolders",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Servers",
schema: "Servers",
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"),
Cpu = table.Column<int>(type: "int", nullable: false),
Memory = table.Column<int>(type: "int", nullable: false),
Disk = table.Column<int>(type: "int", nullable: false),
OverrideStartupCommand = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
OverrideDockerImage = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
VirtualDiskEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
StarId = table.Column<int>(type: "int", nullable: false),
DockerImageIndex = table.Column<int>(type: "int", nullable: false),
NodeId = table.Column<int>(type: "int", nullable: false),
NetworkId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Servers", x => x.Id);
table.ForeignKey(
name: "FK_Servers_Networks_NetworkId",
column: x => x.NetworkId,
principalSchema: "Servers",
principalTable: "Networks",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Servers_Nodes_NodeId",
column: x => x.NodeId,
principalSchema: "Servers",
principalTable: "Nodes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Servers_Stars_StarId",
column: x => x.StarId,
principalSchema: "Servers",
principalTable: "Stars",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "StarDockerImages",
schema: "Servers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Identifier = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DisplayName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
AutoPulling = table.Column<bool>(type: "tinyint(1)", nullable: false),
StarId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_StarDockerImages", x => x.Id);
table.ForeignKey(
name: "FK_StarDockerImages_Stars_StarId",
column: x => x.StarId,
principalSchema: "Servers",
principalTable: "Stars",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "StarVariables",
schema: "Servers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Key = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DefaultValue = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
DisplayName = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
AllowViewing = table.Column<bool>(type: "tinyint(1)", nullable: false),
AllowEditing = table.Column<bool>(type: "tinyint(1)", nullable: false),
Filter = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Type = table.Column<int>(type: "int", nullable: false),
StarId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_StarVariables", x => x.Id);
table.ForeignKey(
name: "FK_StarVariables_Stars_StarId",
column: x => x.StarId,
principalSchema: "Servers",
principalTable: "Stars",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Allocations",
schema: "Servers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
IpAddress = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Port = table.Column<int>(type: "int", nullable: false),
ServerId = table.Column<int>(type: "int", nullable: true),
NodeId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Allocations", x => x.Id);
table.ForeignKey(
name: "FK_Allocations_Nodes_NodeId",
column: x => x.NodeId,
principalSchema: "Servers",
principalTable: "Nodes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Allocations_Servers_ServerId",
column: x => x.ServerId,
principalSchema: "Servers",
principalTable: "Servers",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Backups",
schema: "Servers",
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"),
State = table.Column<int>(type: "int", nullable: false),
Size = table.Column<long>(type: "bigint", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
FinishedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
ServerId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Backups", x => x.Id);
table.ForeignKey(
name: "FK_Backups_Servers_ServerId",
column: x => x.ServerId,
principalSchema: "Servers",
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "ServerVariables",
schema: "Servers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Key = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Value = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ServerId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ServerVariables", x => x.Id);
table.ForeignKey(
name: "FK_ServerVariables_Servers_ServerId",
column: x => x.ServerId,
principalSchema: "Servers",
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Allocations_NodeId",
schema: "Servers",
table: "Allocations",
column: "NodeId");
migrationBuilder.CreateIndex(
name: "IX_Allocations_ServerId",
schema: "Servers",
table: "Allocations",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_Backups_ServerId",
schema: "Servers",
table: "Backups",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_Networks_NodeId",
schema: "Servers",
table: "Networks",
column: "NodeId");
migrationBuilder.CreateIndex(
name: "IX_Servers_NetworkId",
schema: "Servers",
table: "Servers",
column: "NetworkId");
migrationBuilder.CreateIndex(
name: "IX_Servers_NodeId",
schema: "Servers",
table: "Servers",
column: "NodeId");
migrationBuilder.CreateIndex(
name: "IX_Servers_StarId",
schema: "Servers",
table: "Servers",
column: "StarId");
migrationBuilder.CreateIndex(
name: "IX_ServerVariables_ServerId",
schema: "Servers",
table: "ServerVariables",
column: "ServerId");
migrationBuilder.CreateIndex(
name: "IX_StarDockerImages_StarId",
schema: "Servers",
table: "StarDockerImages",
column: "StarId");
migrationBuilder.CreateIndex(
name: "IX_Stars_FolderId",
schema: "Servers",
table: "Stars",
column: "FolderId");
migrationBuilder.CreateIndex(
name: "IX_StarVariables_StarId",
schema: "Servers",
table: "StarVariables",
column: "StarId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Allocations",
schema: "Servers");
migrationBuilder.DropTable(
name: "Backups",
schema: "Servers");
migrationBuilder.DropTable(
name: "ServerVariables",
schema: "Servers");
migrationBuilder.DropTable(
name: "StarDockerImages",
schema: "Servers");
migrationBuilder.DropTable(
name: "StarVariables",
schema: "Servers");
migrationBuilder.DropTable(
name: "Servers",
schema: "Servers");
migrationBuilder.DropTable(
name: "Networks",
schema: "Servers");
migrationBuilder.DropTable(
name: "Stars",
schema: "Servers");
migrationBuilder.DropTable(
name: "Nodes",
schema: "Servers");
migrationBuilder.DropTable(
name: "StarFolders",
schema: "Servers");
}
}
}

View File

@@ -1,516 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MoonlightServers.ApiServer.Database;
#nullable disable
namespace MoonlightServers.ApiServer.Database.Migrations
{
[DbContext(typeof(ServersContext))]
partial class ServersContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Servers")
.HasAnnotation("ProductVersion", "8.0.7")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("IpAddress")
.IsRequired()
.HasColumnType("longtext");
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("Allocations", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Backup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("FinishedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("ServerId")
.HasColumnType("int");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<int>("State")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ServerId");
b.ToTable("Backups", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("NodeId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("NodeId");
b.ToTable("Networks", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("ApiPort")
.HasColumnType("int");
b.Property<string>("Fqdn")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("SslEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Nodes", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("Cpu")
.HasColumnType("int");
b.Property<int>("Disk")
.HasColumnType("int");
b.Property<int>("DockerImageIndex")
.HasColumnType("int");
b.Property<int>("Memory")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("NetworkId")
.HasColumnType("int");
b.Property<int>("NodeId")
.HasColumnType("int");
b.Property<string>("OverrideDockerImage")
.HasColumnType("longtext");
b.Property<string>("OverrideStartupCommand")
.HasColumnType("longtext");
b.Property<int>("StarId")
.HasColumnType("int");
b.Property<bool>("VirtualDiskEnabled")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("NetworkId");
b.HasIndex("NodeId");
b.HasIndex("StarId");
b.ToTable("Servers", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
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", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AllowDockerImageChanging")
.HasColumnType("tinyint(1)");
b.Property<string>("Author")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("DefaultDockerImageIndex")
.HasColumnType("int");
b.Property<string>("DonationUrl")
.HasColumnType("longtext");
b.Property<int?>("FolderId")
.HasColumnType("int");
b.Property<string>("InstallDockerImage")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallScript")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallShell")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("OnlineDetection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("ParseConfiguration")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("RequiredAllocations")
.HasColumnType("int");
b.Property<string>("StartupCommand")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("StopCommand")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("UpdateUrl")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("FolderId");
b.ToTable("Stars", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AutoPulling")
.HasColumnType("tinyint(1)");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Identifier")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("StarId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("StarId");
b.ToTable("StarDockerImages", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarFolder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("StarFolders", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("AllowEditing")
.HasColumnType("tinyint(1)");
b.Property<bool>("AllowViewing")
.HasColumnType("tinyint(1)");
b.Property<string>("DefaultValue")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Filter")
.HasColumnType("longtext");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("StarId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("StarId");
b.ToTable("StarVariables", "Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany("Allocations")
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Allocations")
.HasForeignKey("ServerId");
b.Navigation("Node");
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Backup", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Backups")
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany()
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Node");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Network", "Network")
.WithMany("Servers")
.HasForeignKey("NetworkId");
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node")
.WithMany("Servers")
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star")
.WithMany()
.HasForeignKey("StarId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Network");
b.Navigation("Node");
b.Navigation("Star");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server")
.WithMany("Variables")
.HasForeignKey("ServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Server");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.StarFolder", "Folder")
.WithMany("Stars")
.HasForeignKey("FolderId");
b.Navigation("Folder");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", null)
.WithMany("DockerImages")
.HasForeignKey("StarId");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
{
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", null)
.WithMany("Variables")
.HasForeignKey("StarId");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Network", b =>
{
b.Navigation("Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
{
b.Navigation("Allocations");
b.Navigation("Servers");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
{
b.Navigation("Allocations");
b.Navigation("Backups");
b.Navigation("Variables");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
{
b.Navigation("DockerImages");
b.Navigation("Variables");
});
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarFolder", b =>
{
b.Navigation("Stars");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,13 @@
using Moonlight.ApiServer.Configuration;
using Moonlight.ApiServer.Helpers;
namespace MoonlightServers.ApiServer.Database;
public class MoonlightServersDataContext : DatabaseContext
{
public override string Prefix { get; } = "MoonlightServers";
public MoonlightServersDataContext(AppConfiguration configuration) : base(configuration)
{
}
}

View File

@@ -1,21 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Moonlight.ApiServer.App.Helpers.Database;
using MoonlightServers.ApiServer.Database.Entities;
namespace MoonlightServers.ApiServer.Database;
public class ServersContext : DatabaseContext
{
public override string Prefix => "Servers";
public DbSet<Allocation> Allocations { get; set; }
public DbSet<Backup> Backups { get; set; }
public DbSet<Network> Networks { get; set; }
public DbSet<Node> Nodes { get; set; }
public DbSet<Server> Servers { get; set; }
public DbSet<ServerVariable> ServerVariables { get; set; }
public DbSet<Star> Stars { get; set; }
public DbSet<StarDockerImage> StarDockerImages { get; set; }
public DbSet<StarFolder> StarFolders { get; set; }
public DbSet<StarVariable> StarVariables { get; set; }
}

View File

@@ -1,22 +0,0 @@
using MoonCore.Helpers;
using MoonlightServers.ApiServer.Database.Entities;
namespace MoonlightServers.ApiServer.Extensions;
public static class NodeExtensions
{
public static HttpApiClient CreateClient(this Node node)
{
var httpClient = new HttpClient(new HttpClientHandler() // TODO: Make global http config for proxy etc
{
UseProxy = false
});
var url = $"{(node.SslEnabled ? "https" : "http")}://{node.Fqdn}:{node.ApiPort}/";
httpClient.BaseAddress = new Uri(url);
httpClient.DefaultRequestHeaders.Add("Authorization", node.Token);
return new HttpApiClient(httpClient);
}
}

View File

@@ -1,63 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MoonCore.Extended.Abstractions;
using MoonCore.Helpers;
using Moonlight.ApiServer.App.Attributes;
using Moonlight.ApiServer.App.Exceptions;
using Moonlight.ApiServer.App.Helpers;
using MoonlightServers.ApiServer.Database.Entities;
using MoonlightServers.Shared.Http.Requests.Admin.Allocations;
using MoonlightServers.Shared.Http.Responses.Admin.Allocations;
namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes;
[ApiController]
[Route("admin/servers/nodes/{rootItem:int}/allocations")]
public class NodeAllocationsController : BaseSubCrudController<Node, Allocation, DetailAllocationResponse, CreateAllocationRequest, DetailAllocationResponse, UpdateAllocationRequest, DetailAllocationResponse>
{
public override Func<Node, List<Allocation>> Property => node => node.Allocations;
public NodeAllocationsController(DatabaseRepository<Allocation> itemRepository, DatabaseRepository<Node> rootItemRepository, IHttpContextAccessor contextAccessor) : base(itemRepository, rootItemRepository, contextAccessor)
{
PermissionPrefix = "admin.servers.nodes.allocations";
}
[HttpPost]
[RequirePermission("admin.servers.nodes.allocations.create")]
public override async Task<ActionResult<DetailAllocationResponse>> Create(CreateAllocationRequest request)
{
if (ItemRepository.Get().Any(x => x.IpAddress == request.IpAddress && x.Port == request.Port))
throw new ApiException("An allocation with this ip and port already exists", statusCode: 400);
var item = Mapper.Map<Allocation>(request!);
Property.Invoke(RootItem).Add(item);
RootItemRepository.Update(RootItem);
var response = Mapper.Map<DetailAllocationResponse>(item);
return Ok(response);
}
[HttpPatch("{id}")]
[RequirePermission("admin.servers.nodes.allocations.create")]
public override async Task<ActionResult<DetailAllocationResponse>> Update(int id, UpdateAllocationRequest request)
{
var item = LoadItemById(id);
if (ItemRepository.Get().Any(x => x.IpAddress == request.IpAddress && x.Port == request.Port && x.Id != item.Id))
throw new ApiException("An allocation with this ip and port already exists", statusCode: 400);
var mappedItem = Mapper.Map(item, request!, ignoreNullValues: true);
ItemRepository.Update(mappedItem);
var response = Mapper.Map<DetailAllocationResponse>(mappedItem);
return Ok(response);
}
protected override IEnumerable<Node> IncludeRelations(IQueryable<Node> items)
=> items.Include(x => x.Allocations);
}

View File

@@ -1,105 +0,0 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using MoonCore.Extended.Abstractions;
using MoonCore.Helpers;
using Moonlight.ApiServer.App.Attributes;
using Moonlight.ApiServer.App.Exceptions;
using Moonlight.ApiServer.App.Helpers;
using MoonlightServers.DaemonShared.Http.Resources.Sys;
using MoonlightServers.ApiServer.Database.Entities;
using MoonlightServers.ApiServer.Extensions;
using MoonlightServers.Shared.Http.Requests.Admin.Nodes;
using MoonlightServers.Shared.Http.Responses.Admin.Nodes;
namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes;
[ApiController]
[Route("admin/servers/nodes")]
public class NodesController : BaseCrudController<Node, DetailNodeResponse, CreateNodeRequest, DetailNodeResponse, UpdateNodeRequest, DetailNodeResponse>
{
public NodesController(DatabaseRepository<Node> itemRepository) : base(itemRepository)
{
PermissionPrefix = "admin.servers.nodes";
}
[HttpPost]
[RequirePermission("admin.servers.nodes.create")]
public override async Task<ActionResult<DetailNodeResponse>> Create(CreateNodeRequest request)
{
ValidateFqdn(request.Fqdn, request.SslEnabled);
var node = Mapper.Map<Node>(request);
node.Token = Formatter.GenerateString(32);
var finalNode = ItemRepository.Add(node);
return Ok(Mapper.Map<DetailNodeResponse>(finalNode));
}
[HttpPatch("{id}")]
[RequirePermission("admin.servers.nodes.update")]
public override async Task<ActionResult<DetailNodeResponse>> Update(int id, UpdateNodeRequest request)
{
ValidateFqdn(request.Fqdn, request.SslEnabled);
var item = LoadItemById(id);
item = Mapper.Map(item, request);
ItemRepository.Update(item);
return Ok(Mapper.Map<DetailNodeResponse>(item));
}
[HttpGet("{id}/status")]
[RequirePermission("admin.servers.nodes.status")]
public async Task<ActionResult<StatusNodeResponse>> Status(int id)
{
var node = LoadItemById(id);
using var httpClient = node.CreateClient();
SystemInfoResponse response;
try
{
response = await httpClient.GetJson<SystemInfoResponse>("system/info");
}
catch (HttpRequestException e)
{
throw new ApiException(
"The requested node's api server was not reachable",
e.Message,
statusCode: 502
);
}
var result = Mapper.Map<StatusNodeResponse>(response);
return Ok(result);
}
private void ValidateFqdn(string fqdn, bool ssl)
{
if (ssl)
{
// Is it a valid domain?
if (Regex.IsMatch(fqdn, "^(?!-)(?:[a-zA-Z\\d-]{0,62}[a-zA-Z\\d]\\.)+(?:[a-zA-Z]{2,})$"))
return;
throw new ApiException("The fqdn needs to be a valid domain. If you want to use an ip address as the fqdn, disable ssl for this node", statusCode: 400);
}
else
{
// Is it a valid domain?
if (Regex.IsMatch(fqdn, "^(?!-)(?:[a-zA-Z\\d-]{0,62}[a-zA-Z\\d]\\.)+(?:[a-zA-Z]{2,})$"))
return;
// Is it a valid ip?
if (Regex.IsMatch(fqdn, "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"))
return;
throw new ApiException("The fqdn needs to be either a domain or an ip", statusCode: 400);
}
}
}

View File

@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.ApiServer.Services;
using MoonlightServers.Shared.Http.Responses;
namespace MoonlightServers.ApiServer.Http.Controllers;
[ApiController]
[Route("api/example")]
public class ExampleController : Controller
{
private readonly ExampleService ExampleService;
public ExampleController(ExampleService exampleService)
{
ExampleService = exampleService;
}
[HttpGet]
public async Task<ExampleResponse> Get()
{
var val = await ExampleService.GetValue();
return new ExampleResponse()
{
Number = val
};
}
}

View File

@@ -1,50 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Label="Moonlight Dependencies">
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.7"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MoonCore" Version="1.5.4" />
<PackageReference Include="MoonCore.Extended" Version="1.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />
</ItemGroup>
<ItemGroup Label="Shared references">
<ProjectReference Include="..\MoonlightServers.DaemonShared\MoonlightServers.DaemonShared.csproj" />
<ProjectReference Include="..\MoonlightServers.Shared\MoonlightServers.Shared.csproj" />
</ItemGroup>
<ItemGroup Label="Reference moonlight builds">
<Reference Include="Moonlight.ApiServer">
<HintPath>..\..\Moonlight\Moonlight\ApiServer\bin\Debug\net8.0\Moonlight.ApiServer.dll</HintPath>
</Reference>
<Reference Include="Moonlight.Shared">
<HintPath>..\..\Moonlight\Moonlight\Shared\bin\Debug\net8.0\Moonlight.Shared.dll</HintPath>
</Reference>
<PackageReference Include="Moonlight.ApiServer" Version="2.1.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.11"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Database\Enums\" />
<Folder Include="Database\Migrations\" />
<Folder Include="Helpers\" />
<Folder Include="Http\Hubs\" />
<Folder Include="Http\Middleware\" />
<Folder Include="Implementations\" />
<Folder Include="Models\" />
<Folder Include="Services\" />
<ProjectReference Include="..\MoonlightServers.Frontend\MoonlightServers.Frontend.csproj"/>
<ProjectReference Include="..\MoonlightServers.Shared\MoonlightServers.Shared.csproj"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Database\Entities\"/>
<Folder Include="Database\Migrations\"/>
<Folder Include="Helpers\"/>
<Folder Include="Http\Middleware\"/>
<Folder Include="Implementations\"/>
<Folder Include="Interfaces\"/>
<Folder Include="Models\"/>
</ItemGroup>
</Project>

View File

@@ -1,34 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MoonCore.Extended.Helpers;
using Moonlight.ApiServer.App.PluginApi;
using MoonlightServers.ApiServer.Database;
namespace MoonlightServers.ApiServer;
public class MoonlightServersPlugin : MoonlightPlugin
{
public MoonlightServersPlugin(ILogger logger, PluginService pluginService) : base(logger, pluginService)
{
}
public override Task OnLoaded()
{
return Task.CompletedTask;
}
public override Task OnAppBuilding(WebApplicationBuilder builder, DatabaseHelper databaseHelper)
{
// Register database
builder.Services.AddDbContext<ServersContext>();
databaseHelper.AddDbContext<ServersContext>();
return Task.CompletedTask;
}
public override Task OnAppConfiguring(WebApplication app)
{
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,7 @@
using Moonlight.ApiServer;
var startup = new Startup();
await startup.Run(args, [
typeof(Program).Assembly
]);

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Dev Server": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5269",
"commandLineArgs": "--frontend-asset /css/MoonlightServers.min.css",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"MOONLIGHT_APP_PUBLICURL": "http://localhost:5269"
}
}
}
}

View File

@@ -0,0 +1,22 @@
using MoonCore.Attributes;
namespace MoonlightServers.ApiServer.Services;
[Singleton]
public class ExampleService
{
private readonly Random Random;
private readonly ILogger<ExampleService> Logger;
public ExampleService(ILogger<ExampleService> logger)
{
Logger = logger;
Random = new();
}
public async Task<int> GetValue()
{
Logger.LogInformation("Generating value");
return Random.Next(0, 10324);
}
}

View File

@@ -0,0 +1,15 @@
using Moonlight.ApiServer.Helpers;
using Moonlight.ApiServer.Interfaces.Startup;
using MoonlightServers.ApiServer.Database;
namespace MoonlightServers.ApiServer.Startup;
public class DatabaseStartup : IDatabaseStartup
{
public Task ConfigureDatabase(DatabaseContextCollection collection)
{
collection.Add<MoonlightServersDataContext>();
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,29 @@
using MoonCore.Extensions;
using Moonlight.ApiServer.Interfaces.Startup;
namespace MoonlightServers.ApiServer.Startup;
public class PluginStartup : IAppStartup
{
private readonly ILogger<PluginStartup> Logger;
public PluginStartup(ILogger<PluginStartup> logger)
{
Logger = logger;
}
public Task BuildApp(IHostApplicationBuilder builder)
{
Logger.LogInformation("Elo World from MoonlightServers");
// Scan the current plugin assembly for di services
builder.Services.AutoAddServices<PluginStartup>();
return Task.CompletedTask;
}
public Task ConfigureApp(IApplicationBuilder app)
{
return Task.CompletedTask;
}
}

View File

@@ -1,27 +0,0 @@
using Moonlight.Client.App.Interfaces;
using Moonlight.Client.App.Models;
namespace MoonlightServers.Client.Implementations;
public class ServersSidebarProvider : ISidebarItemProvider
{
public SidebarItem[] GetItems()
{
return [new ()
{
Name = "Servers",
Target = "/servers",
Icon = "bi bi-hdd-rack",
Priority = 5
},
new()
{
Name = "Servers",
Target = "/admin/servers",
Priority = 5,
Icon = "bi bi-hdd-rack",
Group = "Admin",
Permission = "admin.servers.get"
}];
}
}

View File

@@ -1,40 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser"/>
</ItemGroup>
<ItemGroup Label="Moonlight Dependencies">
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0"/>
<PackageReference Include="MoonCore" Version="1.5.4" />
<PackageReference Include="MoonCore.Blazor" Version="1.2.1" />
</ItemGroup>
<ItemGroup Label="Shared references">
<ProjectReference Include="..\MoonlightServers.Shared\MoonlightServers.Shared.csproj" />
</ItemGroup>
<ItemGroup Label="Reference moonlight builds">
<Reference Include="Moonlight.Client">
<HintPath>..\..\Moonlight\Moonlight\Client\bin\Debug\net8.0\Moonlight.Client.dll</HintPath>
</Reference>
<Reference Include="Moonlight.Shared">
<HintPath>..\..\Moonlight\Moonlight\Shared\bin\Debug\net8.0\Moonlight.Shared.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\" />
<Folder Include="Models\" />
<Folder Include="Services\" />
</ItemGroup>
</Project>

View File

@@ -1,31 +0,0 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Logging;
using Moonlight.Client.App.Interfaces;
using Moonlight.Client.App.PluginApi;
using MoonlightServers.Client.Implementations;
namespace MoonlightServers.Client;
public class MoonlightServersClientPlugin : MoonlightClientPlugin
{
public MoonlightServersClientPlugin(ILogger logger, PluginService pluginService) : base(logger, pluginService)
{
}
public override Task OnLoaded()
{
PluginService.RegisterImplementation<ISidebarItemProvider, ServersSidebarProvider>();
return Task.CompletedTask;
}
public override Task OnAppBuilding(WebAssemblyHostBuilder builder)
{
return Task.CompletedTask;
}
public override Task OnAppConfiguring(WebAssemblyHost app)
{
return Task.CompletedTask;
}
}

View File

@@ -1,63 +0,0 @@
@using Moonlight.Client.App.Models.Crud
@using Moonlight.Shared.Http.Resources
@using MoonlightServers.Shared.Http.Requests.Admin.Allocations
@using MoonlightServers.Shared.Http.Responses.Admin.Allocations
@inject HttpApiClient HttpApiClient
<SmartCrud TItem="DetailAllocationResponse"
TCreateForm="CreateAllocationRequest"
TUpdateForm="UpdateAllocationRequest"
OnConfigure="OnConfigure">
<View>
<SmartColumn TItem="DetailAllocationResponse" Field="@(x => x.Id)" Title="Id" />
<SmartColumn TItem="DetailAllocationResponse" Field="@(x => x.IpAddress)" Title="IP Address" />
<SmartColumn TItem="DetailAllocationResponse" Field="@(x => x.Port)" Title="Port" />
</View>
</SmartCrud>
@code
{
[Parameter]
public int NodeId { get; set; }
private void OnConfigure(CrudOptions<DetailAllocationResponse, CreateAllocationRequest, UpdateAllocationRequest> options)
{
options.Loader = async (page, pageSize)
=> await HttpApiClient.GetJson<PagedResponse<DetailAllocationResponse>>($"admin/servers/nodes/{NodeId}/allocations?page={page}&pageSize={pageSize}");
options.CreateFunction = async request => await HttpApiClient.Post($"admin/servers/nodes/{NodeId}/allocations", request);
options.UpdateFunction = async (request, item) => await HttpApiClient.Patch($"admin/servers/nodes/{NodeId}/allocations/{item.Id}", request);
options.DeleteFunction = async item => await HttpApiClient.Delete($"admin/servers/nodes/{NodeId}/allocations/{item.Id}");
options.OnConfigureCreate = option =>
{
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.IpAddress)
.WithColumns(6);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Port)
.WithColumns(6);
};
options.OnConfigureUpdate = (option, item) =>
{
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.IpAddress)
.WithColumns(6);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Port)
.WithColumns(6);
};
}
}

View File

@@ -1,47 +0,0 @@
<div class="mt-5">
<div class="p-3 rounded-lg shadow bg-slate-800">
<div class="flex justify-end">
<div class="flex rounded-md shadow-sm">
<div class="relative flex flex-grow items-stretch focus-within:z-10">
<input type="text" value="50" class="block w-full rounded-none rounded-l-md py-1.5 sm:text-sm sm:leading-6 border-0 bg-slate-800 text-white shadow-sm ring-1 ring-inset ring-white/10 focus:ring-2 focus:ring-inset focus:ring-indigo-500 placeholder-slate-400" placeholder="Lines to fetch">
</div>
<button type="button" class="relative -ml-px inline-flex items-center gap-x-1.5 rounded-r-md px-3 py-2 text-sm font-medium focus:outline-none text-white bg-indigo-600 hover:bg-indigo-700">
<i class="bi bi-arrow-repeat text-lg mr-1"></i>
Refresh
</button>
</div>
</div>
</div>
<div class="p-6 rounded-lg shadow bg-black font-scp text-sm mt-5">
10:43:29 [INF] ExecutionContext Loaded server 44 [23/25] <br />
10:43:29 [INF] ExecutionContext Loaded server 45 [24/25]<br />
10:43:29 [INF] ExecutionContext Loaded server 50 [25/25]<br />
10:43:29 [INF] ExecutionContext Fetched servers. Closing websocket connection<br />
10:43:29 [INF] AsyncTaskMethodBuilder Finishing boot<br />
10:43:29 [INF] AsyncTaskMethodBuilder Restoring docker containers<br />
10:43:29 [INF] ExecutionContext Restored server 35 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 19 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 2 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 9 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 44 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 45 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 42 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 39 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 17 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 15 and reattached stream<br />
10:43:29 [INF] ExecutionContext Restored server 12 and reattached stream<br />
10:50:08 [FTL] ConnectionManager Connection id "0HN65LD3UMODC" application never completed.<br />
11:20:29 [FTL] ConnectionManager Connection id "0HN65LD3UMOE9" application never completed.<br />
15:53:04 [FTL] ConnectionManager Connection id "0HN65LD3UMPS5" application never completed.<br />
15:53:04 [FTL] ConnectionManager Connection id "0HN65LD3UMPN2" application never completed.<br />
15:53:04 [FTL] ConnectionManager Connection id "0HN65LD3UMPN9" application never completed.<br />
15:53:04 [FTL] ConnectionManager Connection id "0HN65LD3UMPRK" application never completed.<br />
20:11:22 [WRN] AsyncTaskMethodBuilder`1 Invalid username format received. Username: 'anonymous', Password: 'IEUser<br />
</div>
</div>
@code
{
[Parameter] public int NodeId { get; set; }
}

View File

@@ -1,117 +0,0 @@
@using System.Text.Json
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
@implements IDisposable
@inject HttpApiClient HttpApiClient
<LazyLoader Load="Load">
<div class="mt-8">
@if (IsOffline)
{
<IconAlert Icon="bi bi-database-x"
Color="text-red-500"
Title="Unable to fetch the node status"
Description="We were unable to fetch the node status. Please check if the node is online and the daemon running">
</IconAlert>
}
else
{
@*
<div class="mt-8 mb-4">
<h2 class="text-base font-semibold leading-7 text-slate-100">
Node overview
</h2>
<p class="mt-1 text-sm leading-6 text-slate-400">
See all important details of this node at one quick look
</p>
</div>
*@
var cpuUsage = Math.Round(Status.CpuUsage.Average(x => x), 2) + "%";
var memoryUsage = $"{Formatter.FormatSize((long)(Status.MemoryTotal - Status.MemoryAvailable))} / {Formatter.FormatSize((long)Status.MemoryTotal)}";
var uptime = Formatter.FormatUptime(Status.Uptime);
var diskUsage = $"{Formatter.FormatSize((long)(Status.DiskTotal - Status.DiskFree))} / {Formatter.FormatSize((long)Status.DiskTotal)}";
<div class="mt-5 gap-5 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
<StatCard Title="CPU Model" Text="@Status.CpuModel" Icon="bi bi-cpu"/>
<StatCard Title="CPU Usage" Text="@cpuUsage" Icon="bi bi-speedometer2"/>
<StatCard Title="Memory Usage" Text="@memoryUsage" Icon="bi bi-memory"/>
<StatCard Title="Host OS" Text="Arch Linux" Icon="bi bi-motherboard"/>
<StatCard Title="Uptime" Text="@uptime" Icon="bi bi-clock-history"/>
<StatCard Title="Containers" Text="11" Icon="bi bi-box-seam"/>
<StatCard Title="Disk Usage" Text="@diskUsage" Icon="bi bi-hdd"/>
<StatCard Title="Version" Text="v2.1 - Galaxy (Release #1)" Icon="bi bi-tag"/>
</div>
<div class="my-4">
<h2 class="text-base font-semibold leading-7 text-slate-100">
CPU Cores
</h2>
<p class="mt-1 text-sm leading-6 text-slate-400">
View the nodes cpu usage in detail
</p>
</div>
<div class="mt-5 gap-5 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5">
@{
int index = 1;
}
@foreach (var usage in Status.CpuUsage)
{
<ProgressStatCard Title="@("Core #" + index)" CurrentValue="@usage" MaxValue="100"></ProgressStatCard>
index++;
}
</div>
}
</div>
</LazyLoader>
@code
{
[Parameter] public int NodeId { get; set; }
private bool IsOffline = false;
private bool KeepRefreshing = true;
private StatusNodeResponse Status;
private async Task Load(LazyLoader arg)
{
await UpdateStatus();
Task.Run(async () =>
{
while (KeepRefreshing)
{
await UpdateStatus();
await InvokeAsync(StateHasChanged);
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
}
private async Task UpdateStatus()
{
IsOffline = false;
try
{
Status = await HttpApiClient.GetJson<StatusNodeResponse>($"admin/servers/nodes/{NodeId}/status");
Console.WriteLine(JsonSerializer.Serialize(Status));
}
catch (Exception)
{
IsOffline = true;
}
}
public void Dispose()
{
KeepRefreshing = false;
}
}

View File

@@ -1,5 +0,0 @@
@page "/admin/servers"
@attribute [RequirePermission("admin.servers.get")]
<NavTabs Index="0" TextSize="text-base" Names="@( ["Servers", "Nodes", "Stars", "Manager"])" Links="@( ["/admin/servers", "/admin/servers/nodes", "/admin/servers/stars", "admin/servers/manager"])"/>

View File

@@ -1,224 +0,0 @@
@page "/admin/servers/nodes"
@using System.Diagnostics
@using MoonCore.Exceptions
@using Moonlight.Client.App.Models.Crud
@using Moonlight.Client.App.Services
@using Moonlight.Shared.Http.Resources
@using MoonlightServers.Client.UI.Components.Forms
@using MoonlightServers.Shared.Http.Requests.Admin.Nodes
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
@using MoonlightServers.Client.UI.Components.Partials
@inject HttpApiClient HttpApiClient
@inject AlertService AlertService
@attribute [RequirePermission("admin.servers.nodes.get")]
<NavTabs Index="1" TextSize="text-base" Names="@( ["Servers", "Nodes", "Stars", "Manager"])" Links="@( ["/admin/servers", "/admin/servers/nodes", "/admin/servers/stars", "admin/servers/manager"])"/>
<div class="mt-5">
<SmartCrud TItem="DetailNodeResponse"
TCreateForm="CreateNodeRequest"
TUpdateForm="UpdateNodeRequest"
OnConfigure="OnConfigure">
<View Context="ViewContext">
<SmartColumn TItem="DetailNodeResponse" Field="@(x => x.Id)" Title="Id"/>
<SmartColumn TItem="DetailNodeResponse" Field="@(x => x.Name)" Title="Name">
<Template>
<a class="text-blue-500" href="#" @onclick:preventDefault @onclick="() => ViewContext.LaunchDetails(context)">@context.Name</a>
</Template>
</SmartColumn>
<SmartColumn TItem="DetailNodeResponse" Field="@(x => x.Fqdn)" Title="FQDN"/>
<SmartColumn TItem="DetailNodeResponse" Field="@(x => x.Id)">
<Template>
<div class="flex justify-between">
@{
NodeFetchState? fetchState;
lock (StatusCache)
fetchState = StatusCache.ContainsKey(context.Id) ? StatusCache[context.Id] : null;
}
@if (fetchState == null)
{
<div class="text-slate-400">Loading status...</div>
}
else if (fetchState.Response != null)
{
var response = fetchState.Response!;
<div class="text-green-500">
<i class="mr-1 align-middle text-lg bi bi-check-circle-fill text-green-500"></i>
<span>Online</span>
</div>
<div>
<i class="bi bi-cpu text-lg text-slate-400 mr-1"></i>
<span class="text-white">
@(Math.Round(response.CpuUsage.Average(x => x), 2))%
</span>
</div>
<div>
<i class="bi bi-memory text-lg text-slate-400 mr-1"></i>
<span class="text-white">
@Formatter.FormatSize((long)(response.MemoryTotal - response.MemoryAvailable))
<span>/</span>
@Formatter.FormatSize((long)response.MemoryTotal)
</span>
</div>
<div>
<i class="bi bi-hdd text-lg text-slate-400 mr-1"></i>
<span class="text-white">
@Formatter.FormatSize((long)(response.DiskTotal - response.DiskFree))
<span>/</span>
@Formatter.FormatSize((long)response.DiskTotal)
</span>
</div>
}
else if(fetchState.Exception != null)
{
<div class="text-white">
<i class="mr-1 align-middle text-lg bi bi-exclamation-triangle-fill text-red-500"></i>
<span class="mr-1">Offline:</span>
<a href="#" @onclick:preventDefault @onclick="() => ShowDetails(fetchState.Exception)" class="mr-1 text-red-400">
Show details
</a>
</div>
}
</div>
</Template>
</SmartColumn>
</View>
<DetailView>
<SmartTabs BarStyle="true" BarText="Details">
<SmartTab Name="Overview">
<NodeOverview NodeId="@context.Id"/>
</SmartTab>
<SmartTab Name="Allocations">
<AllocationEditor NodeId="@context.Id"/>
</SmartTab>
<SmartTab Name="Logs">
<NodeLogs NodeId="@context.Id"/>
</SmartTab>
</SmartTabs>
</DetailView>
</SmartCrud>
</div>
@code
{
private readonly Dictionary<int, NodeFetchState> StatusCache = new();
private void OnConfigure(CrudOptions<DetailNodeResponse, CreateNodeRequest, UpdateNodeRequest> options)
{
options.Loader = async (page, pageSize) =>
{
var response = await HttpApiClient
.GetJson<PagedResponse<DetailNodeResponse>>($"admin/servers/nodes?page={page}&pageSize={pageSize}");
lock (StatusCache)
StatusCache.Clear();
Task.Run(async () =>
{
foreach (var node in response.Items)
{
try
{
var status = await HttpApiClient.GetJson<StatusNodeResponse>($"admin/servers/nodes/{node.Id}/status");
lock (StatusCache)
{
StatusCache.Add(node.Id, new()
{
Response = status
});
}
}
catch (Exception e)
{
lock (StatusCache)
{
StatusCache.Add(node.Id, new()
{
Exception = e
});
}
}
await InvokeAsync(StateHasChanged);
}
});
return response;
};
options.CreateFunction = async request => await HttpApiClient.Post("admin/servers/nodes", request);
options.UpdateFunction = async (request, item) => await HttpApiClient.Patch($"admin/servers/nodes/{item.Id}", request);
options.DeleteFunction = async item => await HttpApiClient.Delete($"admin/servers/nodes/{item.Id}");
options.ShowCreateAsModal = false;
options.ShowUpdateAsModal = false;
options.ShowDetailsAsModal = false;
options.ShowDetailsBar = false;
options.OnConfigureCreate = option =>
{
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Name);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Fqdn);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.ApiPort);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.SslEnabled);
};
options.OnConfigureUpdate = (option, item) =>
{
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Name);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.Fqdn);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.ApiPort);
option
.DefaultPage
.DefaultSection
.AddProperty(x => x.SslEnabled);
};
}
private async Task ShowDetails(Exception e)
{
await AlertService.ErrorLog("Node connection error", e.ToStringDemystified());
}
class NodeFetchState
{
public StatusNodeResponse? Response { get; set; }
public Exception? Exception { get; set; }
}
}

View File

@@ -1,15 +0,0 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using MoonCore.Services
@using MoonCore.Helpers
@using Moonlight.Client
@using Moonlight.Client.App.UI
@using Moonlight.Client.App.UI.Components
@using Moonlight.Client.App.UI.Layouts
@using Moonlight.Client.App.Attributes

View File

@@ -1,6 +0,0 @@
namespace MoonlightServers.Daemon.App.Configuration;
public class AppConfiguration
{
}

View File

@@ -1,109 +0,0 @@
using System.Globalization;
using Mono.Unix.Native;
using MoonCore.Attributes;
namespace MoonlightServers.Daemon.App.Helpers;
[Singleton]
public class HostHelper
{
public async Task<string> GetCpuModel()
{
var lines = await File.ReadAllLinesAsync("/proc/cpuinfo");
foreach (var line in lines)
{
if (line.StartsWith("model name"))
return line.Split(":")[1].Trim();
}
return "Unknown processor";
}
public async Task<double[]> GetCpuUsage()
{
var linesBefore = await File.ReadAllLinesAsync("/proc/stat");
await Task.Delay(1000); // Wait for 1 second
var linesAfter = await File.ReadAllLinesAsync("/proc/stat");
var cpuDataBefore = linesBefore
.Where(line => line.StartsWith("cpu"))
.Select(line => line.Split([" "], StringSplitOptions.RemoveEmptyEntries).Skip(1).Select(long.Parse)
.ToArray())
.ToList();
var cpuDataAfter = linesAfter
.Where(line => line.StartsWith("cpu"))
.Select(line => line.Split([" "], StringSplitOptions.RemoveEmptyEntries).Skip(1).Select(long.Parse)
.ToArray())
.ToList();
var numCores = Environment.ProcessorCount;
var cpuUsagePerCore = new double[numCores];
for (var i = 0; i < numCores; i++)
{
var beforeIdle = cpuDataBefore[i][3];
var beforeTotal = cpuDataBefore[i].Sum();
var afterIdle = cpuDataAfter[i][3];
var afterTotal = cpuDataAfter[i].Sum();
double idleDelta = afterIdle - beforeIdle;
double totalDelta = afterTotal - beforeTotal;
var usage = 100.0 * (1.0 - idleDelta / totalDelta);
cpuUsagePerCore[i] = usage;
}
return cpuUsagePerCore;
}
public async Task<TimeSpan> GetUptime()
{
var uptimeText = await File.ReadAllTextAsync("/proc/uptime");
var values = uptimeText.Split(" ");
var seconds = double.Parse(values[0], CultureInfo.InvariantCulture);
return TimeSpan.FromSeconds(seconds);
}
public async Task<ulong[]> GetMemoryDetails() // 0, total - 1, free - 2, available - 3, cached - 4, swap total - 5, swap free
{
var result = new ulong[6];
var memInfoText = await File.ReadAllLinesAsync("/proc/meminfo");
foreach (var line in memInfoText)
{
if (line.StartsWith("MemTotal:"))
result[0] = 1024 * ulong.Parse(line.Replace("MemTotal:", "").Replace("kB", "").Trim());
if (line.StartsWith("MemFree:"))
result[1] = 1024 * ulong.Parse(line.Replace("MemFree:", "").Replace("kB", "").Trim());
if (line.StartsWith("MemAvailable:"))
result[2] = 1024 * ulong.Parse(line.Replace("MemAvailable:", "").Replace("kB", "").Trim());
if (line.StartsWith("Cached:"))
result[3] = 1024 * ulong.Parse(line.Replace("Cached:", "").Replace("kB", "").Trim());
if (line.StartsWith("SwapTotal:"))
result[4] = 1024 * ulong.Parse(line.Replace("SwapTotal:", "").Replace("kB", "").Trim());
if (line.StartsWith("SwapFree:"))
result[5] = 1024 * ulong.Parse(line.Replace("SwapFree:", "").Replace("kB", "").Trim());
}
return result;
}
public async Task<ulong[]> GetDiskUsage() // 0, Total size - 1, Free size, - 2, Total inodes - 3, free inodes
{
var sysCallRes = Syscall.statvfs("/", out var buf);
if (sysCallRes == -1)
return [0, 0, 0, 0];
return [buf.f_blocks * buf.f_frsize, buf.f_bfree * buf.f_frsize, buf.f_files, buf.f_ffree];
}
}

View File

@@ -1,47 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.Daemon.App.Helpers;
using MoonlightServers.DaemonShared.Http.Resources.Sys;
namespace MoonlightServers.Daemon.App.Http.Controllers.Sys;
[ApiController]
[Route("system/info")]
public class InfoController : Controller
{
private readonly HostHelper HostHelper;
public InfoController(HostHelper hostHelper)
{
HostHelper = hostHelper;
}
[HttpGet]
public async Task<ActionResult<SystemInfoResponse>> Get()
{
var memoryDetails = await HostHelper.GetMemoryDetails();
var diskDetails = await HostHelper.GetDiskUsage();
var response = new SystemInfoResponse()
{
CpuModel = await HostHelper.GetCpuModel(),
CpuUsage = await HostHelper.GetCpuUsage(),
Uptime = await HostHelper.GetUptime(),
MemoryTotal = memoryDetails[0],
MemoryFree = memoryDetails[1],
MemoryAvailable = memoryDetails[2],
MemoryCached = memoryDetails[3],
SwapTotal = memoryDetails[4],
SwapFree = memoryDetails[5],
DiskTotal = diskDetails[0],
DiskFree = diskDetails[1],
DiskTotalInodes = diskDetails[2],
DiskFreeInodes = diskDetails[3]
};
return Ok(response);
}
}

View File

@@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6"/>
<PackageReference Include="MoonCore" Version="1.5.4" />
<PackageReference Include="MoonCore.Unix" Version="1.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MoonlightServers.DaemonShared\MoonlightServers.DaemonShared.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="App\Extensions\" />
<Folder Include="App\Http\Middleware\" />
<Folder Include="App\Implementations\" />
<Folder Include="App\Interfaces\" />
<Folder Include="App\Models\" />
<Folder Include="App\Services\" />
</ItemGroup>
</Project>

View File

@@ -1,34 +0,0 @@
using MoonCore.Extensions;
using MoonCore.Helpers;
var loggerFactory = new LoggerFactory();
var loggerProviders = LoggerBuildHelper.BuildFromConfiguration(configuration =>
{
configuration.Console.Enable = true;
configuration.Console.EnableAnsiMode = true;
configuration.FileLogging.Enable = false;
});
loggerFactory.AddProviders(loggerProviders);
var logger = loggerFactory.CreateLogger("Startup");
logger.LogInformation("Starting MoonlightServers Daemon v2.1 Galaxy"); //TODO: Versions
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddProviders(loggerProviders);
builder.Services.AutoAddServices<Program>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

View File

@@ -1,14 +0,0 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5167",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -1,22 +0,0 @@
namespace MoonlightServers.DaemonShared.Http.Resources.Sys;
public class SystemInfoResponse
{
public string CpuModel { get; set; }
public double[] CpuUsage { get; set; }
public TimeSpan Uptime { get; set; }
public ulong MemoryTotal { get; set; }
public ulong MemoryFree { get; set; }
public ulong MemoryAvailable { get; set; }
public ulong MemoryCached { get; set; }
public ulong SwapTotal { get; set; }
public ulong SwapFree { get; set; }
public ulong DiskTotal { get; set; }
public ulong DiskFree { get; set; }
public ulong DiskTotalInodes { get; set; }
public ulong DiskFreeInodes { get; set; }
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Folder Include="Http\Requests\" />
<Folder Include="Models\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
using Moonlight.Client.Interfaces;
using Moonlight.Client.Models;
namespace MoonlightServers.Frontend.Implementations;
public class SidebarImplementation : ISidebarItemProvider
{
public SidebarItem[] Get()
{
return
[
new SidebarItem()
{
Name = "Example",
Path = "/example",
Icon = "icon-moon",
Group = "MoonlightServers",
Priority = 1
}
];
}
}

View File

@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.11"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.11" PrivateAssets="all"/>
<PackageReference Include="Moonlight.Client" Version="2.1.0"/>
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="wwwroot\css\app.css"/>
<_ContentIncludedByDefault Remove="Pages\Home.razor"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\"/>
<Folder Include="Interfaces\"/>
<Folder Include="UI\Components\"/>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<None Remove="Properties\launchSettings.json"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MoonlightServers.Shared\MoonlightServers.Shared.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
using Moonlight.Client;
var startup = new Startup();
await startup.Run(args, [
typeof(Program).Assembly
]);

View File

@@ -0,0 +1,9 @@
@keyframes shimmer {
0% {
background-position: 0 0
}
to {
background-position: -200% 0
}
}

View File

@@ -0,0 +1,80 @@
/* Buttons */
.btn,
.btn-lg,
.btn-sm,
.btn-xs {
@apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded-lg leading-5 shadow-sm transition;
}
.btn {
@apply px-3 py-2;
}
.btn-lg {
@apply px-4 py-3;
}
.btn-sm {
@apply px-2 py-1;
}
.btn-xs {
@apply px-2 py-0.5;
}
/* Colors */
.btn-primary {
@apply bg-primary-600 hover:bg-primary-500 focus-visible:outline-primary-600;
}
.btn-secondary {
@apply bg-secondary-800 hover:bg-secondary-700 focus-visible:outline-secondary-800;
}
.btn-tertiary {
@apply bg-tertiary-600 hover:bg-tertiary-500 focus-visible:outline-tertiary-600;
}
.btn-danger {
@apply bg-danger-600 hover:bg-danger-500 focus-visible:outline-danger-600;
}
.btn-warning {
@apply bg-warning-500 hover:bg-warning-400 focus-visible:outline-warning-500;
}
.btn-info {
@apply bg-info-600 hover:bg-info-500 focus-visible:outline-info-600;
}
.btn-success {
@apply bg-success-600 hover:bg-success-500 focus-visible:outline-success-600;
}
/* Outline */
.btn-outline-primary {
@apply bg-gray-800 hover:border-gray-600 text-primary-500;
}
.btn-outline-tertiary {
@apply bg-gray-800 hover:border-gray-600 text-tertiary-500;
}
.btn-outline-danger {
@apply bg-gray-800 hover:border-gray-600 text-danger-500;
}
.btn-outline-warning {
@apply bg-gray-800 hover:border-gray-600 text-warning-400;
}
.btn-outline-info {
@apply bg-gray-800 hover:border-gray-600 text-info-500;
}
.btn-outline-success {
@apply bg-gray-800 hover:border-gray-600 text-success-500;
}

View File

@@ -0,0 +1,19 @@
.card {
@apply flex flex-col bg-gray-800 shadow-sm rounded-xl;
}
.card-header {
@apply px-5 py-4 border-b border-gray-700/60 flex items-center;
}
.card-title {
@apply font-semibold text-gray-100;
}
.card-body {
@apply px-5 py-5;
}
.card-footer {
@apply pt-3 pb-3 border-t border-gray-700/60 mt-auto;
}

View File

@@ -0,0 +1,3 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback');
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
@import url("https://cdn.jsdelivr.net/npm/lucide-static@0.460.0/font/lucide.css");

View File

@@ -0,0 +1,77 @@
/* Forms */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
-webkit-appearance: none;
}
.form-input,
.form-textarea,
.form-multiselect,
.form-select,
.form-checkbox,
.form-radio {
@apply bg-gray-700/60 border-2 focus:ring-0 focus:ring-offset-0 disabled:bg-gray-700/30 disabled:border-gray-700 disabled:hover:border-gray-700;
}
.form-checkbox {
@apply rounded;
}
.form-input,
.form-textarea,
.form-multiselect,
.form-select {
@apply text-sm text-gray-100 leading-5 py-2 px-3 border-gray-700 focus:border-primary-500 shadow-sm rounded-lg;
}
.form-input,
.form-textarea {
@apply placeholder-gray-700;
}
.form-select {
@apply pr-10;
}
.form-checkbox,
.form-radio {
@apply text-primary-500 checked:bg-primary-500 checked:border-transparent border border-gray-700/60 focus:border-primary-500/50;
}
/* Switch element */
.form-switch {
@apply relative select-none;
width: 44px;
}
.form-switch label {
@apply block overflow-hidden cursor-pointer h-6 rounded-full;
}
.form-switch label > span:first-child {
@apply absolute block rounded-full;
width: 20px;
height: 20px;
top: 2px;
left: 2px;
right: 50%;
transition: all .15s ease-out;
}
.form-switch input[type="checkbox"]:checked + label {
@apply bg-primary-600;
}
.form-switch input[type="checkbox"]:checked + label > span:first-child {
left: 22px;
}
.form-switch input[type="checkbox"]:disabled + label {
@apply cursor-not-allowed bg-gray-700/20 border border-gray-700/60;
}
.form-switch input[type="checkbox"]:disabled + label > span:first-child {
@apply bg-gray-600;
}

View File

@@ -0,0 +1,19 @@
.loader {
width: 42px;
height: 42px;
border: 3px solid #FFF;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: loader-rotation 1s linear infinite;
}
@keyframes loader-rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,25 @@
.progress {
@apply bg-gray-800 rounded-full overflow-hidden;
}
.progress-bar {
@apply bg-primary-500 rounded-full h-3;
transition: width 0.6s ease;
}
.progress-bar.progress-intermediate {
animation: progress-animation 1s infinite linear;
transform-origin: 0 50%
}
@keyframes progress-animation {
0% {
transform: translateX(0) scaleX(0);
}
40% {
transform: translateX(0) scaleX(0.4);
}
100% {
transform: translateX(100%) scaleX(0.5);
}
}

View File

@@ -0,0 +1,9 @@
* {
scrollbar-width: thin;
scrollbar-color: #64748b transparent;
}
.no-scrollbar {
scrollbar-width: none;
scrollbar-color: transparent transparent;
}

View File

@@ -0,0 +1 @@
npx tailwindcss -i style.css -o ../wwwroot/css/MoonlightServers.min.css --watch

View File

@@ -0,0 +1,2 @@
#! /bin/bash
npx tailwindcss -i style.css -o ../wwwroot/css/MoonlightServers.min.css --watch

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
{
"devDependencies": {
"tailwindcss": "^3.4.11"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.9"
}
}

View File

@@ -0,0 +1,79 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "additions/animations.css";
@import "additions/fonts.css";
@import "additions/buttons.css";
@import "additions/cards.css";
@import "additions/forms.css";
@import "additions/progress.css";
@import "additions/scrollbar.css";
@import "additions/loaders.css";
@import "tailwindcss/utilities";
#blazor-error-ui {
display: none;
}
#loader {
display: block;
width: 10rem;
height: 10rem;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #9370DB;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
@apply border-t-primary-500
}
#loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
-webkit-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
@apply border-t-tertiary-500
}
#loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
@apply border-t-info-500
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,128 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
corePlugins: {
preflight: false,
},
content: [
'../**/*.razor',
'mappings/*.map'
],
theme: {
extend: {
fontFamily: {
inter: ['Inter', 'sans-serif'],
scp: ['Source Code Pro', 'mono'],
},
colors: {
primary: {
50: '#eef2ff',
100: '#e0e7ff',
200: '#c7d2fe',
300: '#a5b4fc',
400: '#818cf8',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
800: '#3730a3',
900: '#312e81',
950: '#1e1b4b'
},
secondary: {
100: '#F9F9F9',
200: '#F1F1F2',
300: '#DBDFE9',
400: '#B5B5C3',
500: '#99A1B7',
600: '#707993',
700: '#444e6b',
750: '#293249',
800: '#1c2438',
900: '#111721',
950: '#0e121c',
},
tertiary: {
50: '#f5f3ff',
100: '#ede9fe',
200: '#ddd6fe',
300: '#c4b5fd',
400: '#a78bfa',
500: '#8b5cf6',
600: '#7c3aed',
700: '#6d28d9',
800: '#5b21b6',
900: '#4c1d95',
950: '#2e1065'
},
warning: {
50: '#fefce8',
100: '#fef9c3',
200: '#fef08a',
300: '#fde047',
400: '#facc15',
500: '#eab308',
600: '#ca8a04',
700: '#a16207',
800: '#854d0e',
900: '#713f12',
950: '#422006'
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
950: '#450a0a'
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
950: '#052e16'
},
info: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554'
},
gray: {
100: '#F9F9F9',
200: '#F1F1F2',
300: '#DBDFE9',
400: '#B5B5C3',
500: '#99A1B7',
600: '#707993',
700: '#444e6b',
750: '#293249',
800: '#1c2438',
900: '#111721',
950: '#0e121c',
}
},
animation: {
'shimmer': 'shimmer 2s linear infinite',
}
},
}
}

View File

@@ -0,0 +1,25 @@
@page "/example"
@using MoonCore.Helpers
@using MoonCore.Blazor.Tailwind.Components
@using MoonlightServers.Shared.Http.Responses
@inject HttpApiClient ApiClient
<h3 class="text-primary-500 mb-8">Welcome to this example page</h3>
<LazyLoader Load="Load">
<h1 class="text-center text-zinc-500">
@Response.Number
</h1>
</LazyLoader>
@code
{
private ExampleResponse Response;
private async Task Load(LazyLoader _)
{
Response = await ApiClient.GetJson<ExampleResponse>("api/example");
}
}

View File

@@ -1,8 +0,0 @@
namespace MoonlightServers.Shared.Enums;
public enum BackupState
{
Creating = 0,
Failed = 1,
Created = 2
}

View File

@@ -1,9 +0,0 @@
namespace MoonlightServers.Shared.Enums;
public enum StarVariableType
{
Text = 0,
Number = 1,
Toggle = 2,
Select = 3
}

View File

@@ -1,14 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MoonlightServers.Shared.Http.Requests.Admin.Allocations;
public class CreateAllocationRequest
{
[Required(ErrorMessage = "You need to provide an ip address")]
[RegularExpression("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$", ErrorMessage = "You need tp provide a valid ip address")]
public string IpAddress { get; set; } = "0.0.0.0";
[Required(ErrorMessage = "You need to provgide a port")]
[Range(1, 65535 , ErrorMessage = "You need to provide a valid port")]
public int Port { get; set; }
}

View File

@@ -1,14 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MoonlightServers.Shared.Http.Requests.Admin.Allocations;
public class UpdateAllocationRequest
{
[Required(ErrorMessage = "You need to provide an ip address")]
[RegularExpression("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$", ErrorMessage = "You need tp provide a valid ip address")]
public string IpAddress { get; set; } = "0.0.0.0";
[Required(ErrorMessage = "You need to provgide a port")]
[Range(1, 65535 , ErrorMessage = "You need to provide a valid port")]
public int Port { get; set; }
}

View File

@@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MoonlightServers.Shared.Http.Requests.Admin.Nodes;
public class CreateNodeRequest
{
[Required(ErrorMessage = "You need to provide a name")]
public string Name { get; set; }
[Required(ErrorMessage = "You need to provide a fqdn")]
public string Fqdn { get; set; }
[Range(1, 65535, ErrorMessage = "You need to provide a valid port")]
public int ApiPort { get; set; } = 8080;
public bool SslEnabled { get; set; } = false;
}

View File

@@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace MoonlightServers.Shared.Http.Requests.Admin.Nodes;
public class UpdateNodeRequest
{
[Required(ErrorMessage = "You need to provide a name")]
public string Name { get; set; }
[Required(ErrorMessage = "You need to provide a fqdn")]
public string Fqdn { get; set; }
[Range(1, 65535, ErrorMessage = "You need to provide a valid port")]
public int ApiPort { get; set; } = 8080;
public bool SslEnabled { get; set; } = false;
}

View File

@@ -1,8 +0,0 @@
namespace MoonlightServers.Shared.Http.Responses.Admin.Allocations;
public class DetailAllocationResponse
{
public int Id { get; set; }
public string IpAddress { get; set; } = "0.0.0.0";
public int Port { get; set; }
}

View File

@@ -1,11 +0,0 @@
namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes;
public class DetailNodeResponse
{
public int Id { get; set; }
public string Name { get; set; }
public string Fqdn { get; set; }
public int ApiPort { get; set; }
public bool SslEnabled { get; set; }
}

View File

@@ -1,22 +0,0 @@
namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes;
public class StatusNodeResponse
{
public string CpuModel { get; set; }
public double[] CpuUsage { get; set; }
public TimeSpan Uptime { get; set; }
public ulong MemoryTotal { get; set; }
public ulong MemoryFree { get; set; }
public ulong MemoryAvailable { get; set; }
public ulong MemoryCached { get; set; }
public ulong SwapTotal { get; set; }
public ulong SwapFree { get; set; }
public ulong DiskTotal { get; set; }
public ulong DiskFree { get; set; }
public ulong DiskTotalInodes { get; set; }
public ulong DiskFreeInodes { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace MoonlightServers.Shared.Http.Responses;
public class ExampleResponse
{
public int Number { get; set; }
}

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="Http\Requests\"/>
</ItemGroup>
</Project>

View File

@@ -1,14 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Shared", "MoonlightServers.Shared\MoonlightServers.Shared.csproj", "{E16B0418-F865-41A6-A532-A2FF15651894}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.ApiServer", "MoonlightServers.ApiServer\MoonlightServers.ApiServer.csproj", "{EAF24574-889F-41B8-85AA-7C0B856137BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.ApiServer", "MoonlightServers.ApiServer\MoonlightServers.ApiServer.csproj", "{74EB54DC-0136-4A02-A888-EB69B4905631}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Frontend", "MoonlightServers.Frontend\MoonlightServers.Frontend.csproj", "{E7E39DA0-F920-4329-87FB-CBAF129B6835}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Client", "MoonlightServers.Client\MoonlightServers.Client.csproj", "{0D44004B-087F-4F4E-9346-9D079E40755E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Daemon", "MoonlightServers.Daemon\MoonlightServers.Daemon.csproj", "{84E74090-85F3-4CCF-BD12-E533BDB869D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.DaemonShared", "MoonlightServers.DaemonShared\MoonlightServers.DaemonShared.csproj", "{FCA17F9E-3734-4E94-8C6C-997012EBAB88}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Shared", "MoonlightServers.Shared\MoonlightServers.Shared.csproj", "{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -16,25 +12,17 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E16B0418-F865-41A6-A532-A2FF15651894}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E16B0418-F865-41A6-A532-A2FF15651894}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E16B0418-F865-41A6-A532-A2FF15651894}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E16B0418-F865-41A6-A532-A2FF15651894}.Release|Any CPU.Build.0 = Release|Any CPU
{74EB54DC-0136-4A02-A888-EB69B4905631}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74EB54DC-0136-4A02-A888-EB69B4905631}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74EB54DC-0136-4A02-A888-EB69B4905631}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74EB54DC-0136-4A02-A888-EB69B4905631}.Release|Any CPU.Build.0 = Release|Any CPU
{0D44004B-087F-4F4E-9346-9D079E40755E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D44004B-087F-4F4E-9346-9D079E40755E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D44004B-087F-4F4E-9346-9D079E40755E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D44004B-087F-4F4E-9346-9D079E40755E}.Release|Any CPU.Build.0 = Release|Any CPU
{84E74090-85F3-4CCF-BD12-E533BDB869D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84E74090-85F3-4CCF-BD12-E533BDB869D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84E74090-85F3-4CCF-BD12-E533BDB869D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84E74090-85F3-4CCF-BD12-E533BDB869D8}.Release|Any CPU.Build.0 = Release|Any CPU
{FCA17F9E-3734-4E94-8C6C-997012EBAB88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCA17F9E-3734-4E94-8C6C-997012EBAB88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCA17F9E-3734-4E94-8C6C-997012EBAB88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCA17F9E-3734-4E94-8C6C-997012EBAB88}.Release|Any CPU.Build.0 = Release|Any CPU
{EAF24574-889F-41B8-85AA-7C0B856137BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAF24574-889F-41B8-85AA-7C0B856137BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAF24574-889F-41B8-85AA-7C0B856137BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAF24574-889F-41B8-85AA-7C0B856137BF}.Release|Any CPU.Build.0 = Release|Any CPU
{E7E39DA0-F920-4329-87FB-CBAF129B6835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7E39DA0-F920-4329-87FB-CBAF129B6835}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7E39DA0-F920-4329-87FB-CBAF129B6835}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7E39DA0-F920-4329-87FB-CBAF129B6835}.Release|Any CPU.Build.0 = Release|Any CPU
{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -1,2 +0,0 @@
# Servers
A moonlight module allowing you to run programs and game servers in docker containers while offering an easy to use ui for them