Added authentication for the node against the api server. Cleaned up routes
This commit is contained in:
@@ -14,6 +14,7 @@ public class Node
|
|||||||
// Connection details
|
// Connection details
|
||||||
public string Fqdn { get; set; }
|
public string Fqdn { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
public string TokenId { get; set; }
|
||||||
public int HttpPort { get; set; }
|
public int HttpPort { get; set; }
|
||||||
public int FtpPort { get; set; }
|
public int FtpPort { get; set; }
|
||||||
public bool UseSsl { get; set; }
|
public bool UseSsl { get; set; }
|
||||||
|
|||||||
456
MoonlightServers.ApiServer/Database/Migrations/20250301142415_AddedTokenIdField.Designer.cs
generated
Normal file
456
MoonlightServers.ApiServer/Database/Migrations/20250301142415_AddedTokenIdField.Designer.cs
generated
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using MoonlightServers.ApiServer.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.ApiServer.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ServersDataContext))]
|
||||||
|
[Migration("20250301142415_AddedTokenIdField")]
|
||||||
|
partial class AddedTokenIdField
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.11")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("NodeId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Port")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NodeId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_Allocations", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("EnableDynamicFirewall")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableTransparentMode")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Fqdn")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("FtpPort")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("HttpPort")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("UseSsl")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Servers_Nodes", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("Bandwidth")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Cpu")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Disk")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("DockerImageIndex")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Memory")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("NodeId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("OwnerId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("StarId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("StartupOverride")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("UseVirtualDisk")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NodeId");
|
||||||
|
|
||||||
|
b.HasIndex("StarId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_Servers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("Completed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<long>("Size")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<bool>("Successful")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_ServerBackups", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("ServerId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_ServerVariables", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AllowDockerImageChange")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("DefaultDockerImage")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("DonateUrl")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("InstallDockerImage")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("InstallScript")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("InstallShell")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("OnlineDetection")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ParseConfiguration")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("RequiredAllocations")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("StartupCommand")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("StopCommand")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UpdateUrl")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Servers_Stars", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AutoPulling")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Identifier")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("StarId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("StarId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_StarDockerImages", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AllowEditing")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowViewing")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("DefaultValue")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Filter")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("StarId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("StarId");
|
||||||
|
|
||||||
|
b.ToTable("Servers_StarVariables", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
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.Server", b =>
|
||||||
|
{
|
||||||
|
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("Node");
|
||||||
|
|
||||||
|
b.Navigation("Star");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", null)
|
||||||
|
.WithMany("Backups")
|
||||||
|
.HasForeignKey("ServerId");
|
||||||
|
});
|
||||||
|
|
||||||
|
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.StarDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star")
|
||||||
|
.WithMany("DockerImages")
|
||||||
|
.HasForeignKey("StarId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Star");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star")
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("StarId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Star");
|
||||||
|
});
|
||||||
|
|
||||||
|
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");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.ApiServer.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedTokenIdField : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "TokenId",
|
||||||
|
table: "Servers_Nodes",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TokenId",
|
||||||
|
table: "Servers_Nodes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,6 +84,10 @@ namespace MoonlightServers.ApiServer.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<bool>("UseSsl")
|
b.Property<bool>("UseSsl")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public class NodesController : Controller
|
|||||||
var node = Mapper.Map<Node>(request);
|
var node = Mapper.Map<Node>(request);
|
||||||
|
|
||||||
node.Token = Formatter.GenerateString(32);
|
node.Token = Formatter.GenerateString(32);
|
||||||
|
node.TokenId = Formatter.GenerateString(6);
|
||||||
|
|
||||||
var finalNode = await NodeRepository.Add(node);
|
var finalNode = await NodeRepository.Add(node);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Http.Controllers.Remote.Node;
|
namespace MoonlightServers.ApiServer.Http.Controllers.Remote.Nodes;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/servers/remote/node")]
|
[Route("api/remote/server/node")]
|
||||||
|
[Authorize(AuthenticationSchemes = "serverNodeAuthentication")]
|
||||||
public class NodeTripController : Controller
|
public class NodeTripController : Controller
|
||||||
{
|
{
|
||||||
[HttpGet("trip")]
|
[HttpGet("trip")]
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
@@ -6,35 +7,45 @@ using MoonCore.Models;
|
|||||||
using MoonlightServers.ApiServer.Database.Entities;
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Http.Controllers.Remote.Servers;
|
namespace MoonlightServers.ApiServer.Http.Controllers.Remote;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/servers/remote/servers")]
|
[Route("api/remote/servers")]
|
||||||
public class RemoteServersController : Controller
|
[Authorize(AuthenticationSchemes = "serverNodeAuthentication")]
|
||||||
|
public class ServersController : Controller
|
||||||
{
|
{
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
private readonly DatabaseRepository<Server> ServerRepository;
|
||||||
private readonly ILogger<RemoteServersController> Logger;
|
private readonly DatabaseRepository<Node> NodeRepository;
|
||||||
|
private readonly ILogger<ServersController> Logger;
|
||||||
|
|
||||||
public RemoteServersController(
|
public ServersController(
|
||||||
DatabaseRepository<Server> serverRepository,
|
DatabaseRepository<Server> serverRepository,
|
||||||
ILogger<RemoteServersController> logger
|
DatabaseRepository<Node> nodeRepository,
|
||||||
)
|
ILogger<ServersController> logger)
|
||||||
{
|
{
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
|
NodeRepository = nodeRepository;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<PagedData<ServerDataResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
public async Task<PagedData<ServerDataResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
||||||
{
|
{
|
||||||
|
// Load the node via the token id
|
||||||
|
var tokenId = User.Claims.First(x => x.Type == "iss").Value;
|
||||||
|
|
||||||
|
var node = await NodeRepository
|
||||||
|
.Get()
|
||||||
|
.FirstAsync(x => x.TokenId == tokenId);
|
||||||
|
|
||||||
var total = await ServerRepository
|
var total = await ServerRepository
|
||||||
.Get()
|
.Get()
|
||||||
.Where(x => x.Node.Id == 1)
|
.Where(x => x.Node.Id == node.Id)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
var servers = await ServerRepository
|
var servers = await ServerRepository
|
||||||
.Get()
|
.Get()
|
||||||
.Where(x => x.Node.Id == 1)
|
.Where(x => x.Node.Id == node.Id)
|
||||||
.Include(x => x.Star)
|
.Include(x => x.Star)
|
||||||
.ThenInclude(x => x.DockerImages)
|
.ThenInclude(x => x.DockerImages)
|
||||||
.Include(x => x.Variables)
|
.Include(x => x.Variables)
|
||||||
@@ -48,12 +59,14 @@ public class RemoteServersController : Controller
|
|||||||
foreach (var server in servers)
|
foreach (var server in servers)
|
||||||
{
|
{
|
||||||
var dockerImage = server.Star.DockerImages
|
var dockerImage = server.Star.DockerImages
|
||||||
.FirstOrDefault(x => x.Id == server.DockerImageIndex);
|
.Skip(server.DockerImageIndex)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (dockerImage == null)
|
if (dockerImage == null)
|
||||||
{
|
{
|
||||||
dockerImage = server.Star.DockerImages
|
dockerImage = server.Star.DockerImages
|
||||||
.FirstOrDefault(x => x.Id == server.Star.DefaultDockerImage);
|
.Skip(server.Star.DefaultDockerImage)
|
||||||
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dockerImage == null)
|
if (dockerImage == null)
|
||||||
@@ -101,8 +114,18 @@ public class RemoteServersController : Controller
|
|||||||
[HttpGet("{id:int}/install")]
|
[HttpGet("{id:int}/install")]
|
||||||
public async Task<ServerInstallDataResponse> GetInstall([FromRoute] int id)
|
public async Task<ServerInstallDataResponse> GetInstall([FromRoute] int id)
|
||||||
{
|
{
|
||||||
|
// Load the node via the token id
|
||||||
|
var tokenId = User.Claims.First(x => x.Type == "iss").Value;
|
||||||
|
|
||||||
|
var node = await NodeRepository
|
||||||
|
.Get()
|
||||||
|
.FirstAsync(x => x.TokenId == tokenId);
|
||||||
|
|
||||||
|
// Load the server with the star data attached. We filter by the node to ensure the node can only access
|
||||||
|
// servers linked to it
|
||||||
var server = await ServerRepository
|
var server = await ServerRepository
|
||||||
.Get()
|
.Get()
|
||||||
|
.Where(x => x.Node.Id == node.Id)
|
||||||
.Include(x => x.Star)
|
.Include(x => x.Star)
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using MoonCore.Extended.Abstractions;
|
||||||
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
|
|
||||||
|
namespace MoonlightServers.ApiServer.Implementations;
|
||||||
|
|
||||||
|
public class NodeJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider ServiceProvider;
|
||||||
|
|
||||||
|
public NodeJwtBearerOptions(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
ServiceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(JwtBearerOptions options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(string? name, JwtBearerOptions options)
|
||||||
|
{
|
||||||
|
// Dont configure any other scheme
|
||||||
|
if (name != "serverNodeAuthentication")
|
||||||
|
return;
|
||||||
|
|
||||||
|
options.TokenValidationParameters.IssuerSigningKeyResolver = (_, _, kid, _) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(kid))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (kid.Length != 6)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
using var scope = ServiceProvider.CreateScope();
|
||||||
|
|
||||||
|
var nodeRepo = scope.ServiceProvider.GetRequiredService<DatabaseRepository<Node>>();
|
||||||
|
|
||||||
|
var node = nodeRepo
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.TokenId == kid);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new SymmetricSecurityKey(
|
||||||
|
Encoding.UTF8.GetBytes(node.Token)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
<Folder Include="Database\Migrations\"/>
|
<Folder Include="Database\Migrations\"/>
|
||||||
<Folder Include="Helpers\"/>
|
<Folder Include="Helpers\"/>
|
||||||
<Folder Include="Http\Middleware\"/>
|
<Folder Include="Http\Middleware\"/>
|
||||||
<Folder Include="Implementations\"/>
|
|
||||||
<Folder Include="Interfaces\"/>
|
<Folder Include="Interfaces\"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using MoonCore.Extensions;
|
using MoonCore.Extensions;
|
||||||
using Moonlight.ApiServer.Interfaces.Startup;
|
using Moonlight.ApiServer.Interfaces.Startup;
|
||||||
using MoonlightServers.ApiServer.Database;
|
using MoonlightServers.ApiServer.Database;
|
||||||
|
using MoonlightServers.ApiServer.Implementations;
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Startup;
|
namespace MoonlightServers.ApiServer.Startup;
|
||||||
|
|
||||||
@@ -13,6 +16,24 @@ public class PluginStartup : IPluginStartup
|
|||||||
|
|
||||||
builder.Services.AddDbContext<ServersDataContext>();
|
builder.Services.AddDbContext<ServersDataContext>();
|
||||||
|
|
||||||
|
// Configure authentication for the remote endpoints
|
||||||
|
builder.Services
|
||||||
|
.AddAuthentication()
|
||||||
|
.AddJwtBearer("serverNodeAuthentication", options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new()
|
||||||
|
{
|
||||||
|
ClockSkew = TimeSpan.Zero,
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateActor = false,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuerSigningKey = true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, NodeJwtBearerOptions>();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,7 @@ public partial class Server
|
|||||||
|
|
||||||
// Fetching remote configuration
|
// Fetching remote configuration
|
||||||
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
|
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
|
||||||
using var remoteHttpClient = await remoteService.CreateHttpClient();
|
var installData = await remoteService.GetServerInstallation(Configuration.Id);
|
||||||
|
|
||||||
var installData =
|
|
||||||
await remoteHttpClient.GetJson<ServerInstallDataResponse>(
|
|
||||||
$"api/servers/remote/servers/{Configuration.Id}/install");
|
|
||||||
|
|
||||||
var dockerImageService = ServiceProvider.GetRequiredService<DockerImageService>();
|
var dockerImageService = ServiceProvider.GetRequiredService<DockerImageService>();
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public class AppConfiguration
|
|||||||
public class SecurityData
|
public class SecurityData
|
||||||
{
|
{
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
public string TokenId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StorageData
|
public class StorageData
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
using Docker.DotNet.Models;
|
|
||||||
using Mono.Unix.Native;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonlightServers.Daemon.Configuration;
|
|
||||||
using MoonlightServers.Daemon.Models.Cache;
|
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.Helpers;
|
|
||||||
|
|
||||||
public static class ServerConfigurationHelper
|
|
||||||
{
|
|
||||||
public static void ApplyRuntimeOptions(CreateContainerParameters parameters, ServerConfiguration configuration, AppConfiguration appConfiguration)
|
|
||||||
{
|
|
||||||
ApplySharedOptions(parameters, configuration);
|
|
||||||
|
|
||||||
// -- Cap drops
|
|
||||||
parameters.HostConfig.CapDrop = new List<string>()
|
|
||||||
{
|
|
||||||
"setpcap", "mknod", "audit_write", "net_raw", "dac_override",
|
|
||||||
"fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap"
|
|
||||||
};
|
|
||||||
|
|
||||||
// -- More security options
|
|
||||||
parameters.HostConfig.ReadonlyRootfs = true;
|
|
||||||
parameters.HostConfig.SecurityOpt = new List<string>()
|
|
||||||
{
|
|
||||||
"no-new-privileges"
|
|
||||||
};
|
|
||||||
|
|
||||||
// - Name
|
|
||||||
var name = $"moonlight-runtime-{configuration.Id}";
|
|
||||||
parameters.Name = name;
|
|
||||||
parameters.Hostname = name;
|
|
||||||
|
|
||||||
// - Image
|
|
||||||
parameters.Image = configuration.DockerImage;
|
|
||||||
|
|
||||||
// - Env
|
|
||||||
parameters.Env = ConstructEnv(configuration)
|
|
||||||
.Select(x => $"{x.Key}={x.Value}")
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// -- Working directory
|
|
||||||
parameters.WorkingDir = "/home/container";
|
|
||||||
|
|
||||||
// - User
|
|
||||||
var userId = Syscall.getuid();
|
|
||||||
|
|
||||||
if (userId == 0)
|
|
||||||
{
|
|
||||||
// We are running as root, so we need to run the container as another user and chown the files when we make changes
|
|
||||||
parameters.User = $"998:998";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We are not running as root, so we start the container as the same user,
|
|
||||||
// as we are not able to chown the container content to a different user
|
|
||||||
parameters.User = $"{userId}:{userId}";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -- Mounts
|
|
||||||
parameters.HostConfig.Mounts = new List<Mount>();
|
|
||||||
|
|
||||||
parameters.HostConfig.Mounts.Add(new()
|
|
||||||
{
|
|
||||||
Source = GetRuntimeVolume(configuration, appConfiguration),
|
|
||||||
Target = "/home/container",
|
|
||||||
ReadOnly = false,
|
|
||||||
Type = "bind"
|
|
||||||
});
|
|
||||||
|
|
||||||
// -- Ports
|
|
||||||
//var config = configService.Get();
|
|
||||||
|
|
||||||
if (true) // TODO: Add network toggle
|
|
||||||
{
|
|
||||||
parameters.ExposedPorts = new Dictionary<string, EmptyStruct>();
|
|
||||||
parameters.HostConfig.PortBindings = new Dictionary<string, IList<PortBinding>>();
|
|
||||||
|
|
||||||
foreach (var allocation in configuration.Allocations)
|
|
||||||
{
|
|
||||||
parameters.ExposedPorts.Add($"{allocation.Port}/tcp", new());
|
|
||||||
parameters.ExposedPorts.Add($"{allocation.Port}/udp", new());
|
|
||||||
|
|
||||||
parameters.HostConfig.PortBindings.Add($"{allocation.Port}/tcp", new List<PortBinding>
|
|
||||||
{
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
HostPort = allocation.Port.ToString(),
|
|
||||||
HostIP = allocation.IpAddress
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
parameters.HostConfig.PortBindings.Add($"{allocation.Port}/udp", new List<PortBinding>
|
|
||||||
{
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
HostPort = allocation.Port.ToString(),
|
|
||||||
HostIP = allocation.IpAddress
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ApplySharedOptions(CreateContainerParameters parameters, ServerConfiguration configuration)
|
|
||||||
{
|
|
||||||
// - Input, output & error streams and tty
|
|
||||||
parameters.Tty = true;
|
|
||||||
parameters.AttachStderr = true;
|
|
||||||
parameters.AttachStdin = true;
|
|
||||||
parameters.AttachStdout = true;
|
|
||||||
parameters.OpenStdin = true;
|
|
||||||
|
|
||||||
// - Host config
|
|
||||||
parameters.HostConfig = new HostConfig();
|
|
||||||
|
|
||||||
// -- CPU limits
|
|
||||||
parameters.HostConfig.CPUQuota = configuration.Cpu * 1000;
|
|
||||||
parameters.HostConfig.CPUPeriod = 100000;
|
|
||||||
parameters.HostConfig.CPUShares = 1024;
|
|
||||||
|
|
||||||
// -- Memory and swap limits
|
|
||||||
var memoryLimit = configuration.Memory;
|
|
||||||
|
|
||||||
// The overhead multiplier gives the container a little bit more memory to prevent crashes
|
|
||||||
var memoryOverhead = memoryLimit + (memoryLimit * 0.05f); // TODO: Config
|
|
||||||
|
|
||||||
long swapLimit = -1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
// If swap is enabled globally and not disabled on this server, set swap
|
|
||||||
if (!configuration.Limits.DisableSwap && config.Server.EnableSwap)
|
|
||||||
swapLimit = (long)(memoryOverhead + memoryOverhead * config.Server.SwapMultiplier);
|
|
||||||
co
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Finalize limits by converting and updating the host config
|
|
||||||
parameters.HostConfig.Memory = ByteConverter.FromMegaBytes((long)memoryOverhead, 1000).Bytes;
|
|
||||||
parameters.HostConfig.MemoryReservation = ByteConverter.FromMegaBytes(memoryLimit, 1000).Bytes;
|
|
||||||
parameters.HostConfig.MemorySwap = swapLimit == -1 ? swapLimit : ByteConverter.FromMegaBytes(swapLimit, 1000).Bytes;
|
|
||||||
|
|
||||||
// -- Other limits
|
|
||||||
parameters.HostConfig.BlkioWeight = 100;
|
|
||||||
//container.HostConfig.PidsLimit = configuration.Limits.PidsLimit;
|
|
||||||
parameters.HostConfig.OomKillDisable = true; //!configuration.Limits.EnableOomKill;
|
|
||||||
|
|
||||||
// -- DNS
|
|
||||||
parameters.HostConfig.DNS = /*config.Docker.DnsServers.Any() ? config.Docker.DnsServers :*/ new List<string>()
|
|
||||||
{
|
|
||||||
"1.1.1.1",
|
|
||||||
"9.9.9.9"
|
|
||||||
};
|
|
||||||
|
|
||||||
// -- Tmpfs
|
|
||||||
parameters.HostConfig.Tmpfs = new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "/tmp", $"rw,exec,nosuid,size=100M" } // TODO: Config
|
|
||||||
};
|
|
||||||
|
|
||||||
// -- Logging
|
|
||||||
parameters.HostConfig.LogConfig = new()
|
|
||||||
{
|
|
||||||
Type = "json-file", // We need to use this provider, as the GetLogs endpoint needs it
|
|
||||||
Config = new Dictionary<string, string>()
|
|
||||||
};
|
|
||||||
|
|
||||||
// - Labels
|
|
||||||
parameters.Labels = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
parameters.Labels.Add("Software", "Moonlight-Panel");
|
|
||||||
parameters.Labels.Add("ServerId", configuration.Id.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<string, string> ConstructEnv(ServerConfiguration configuration)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
// Default environment variables
|
|
||||||
//TODO: Add timezone, add server ip
|
|
||||||
result.Add("STARTUP", configuration.StartupCommand);
|
|
||||||
result.Add("SERVER_MEMORY", configuration.Memory.ToString());
|
|
||||||
|
|
||||||
if (configuration.Allocations.Length > 0)
|
|
||||||
{
|
|
||||||
var mainAllocation = configuration.Allocations.First();
|
|
||||||
|
|
||||||
result.Add("SERVER_IP", mainAllocation.IpAddress);
|
|
||||||
result.Add("SERVER_PORT", mainAllocation.Port.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle additional allocation variables
|
|
||||||
var i = 1;
|
|
||||||
foreach (var additionalAllocation in configuration.Allocations)
|
|
||||||
{
|
|
||||||
result.Add($"ML_PORT_{i}", additionalAllocation.Port.ToString());
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy variables as env vars
|
|
||||||
foreach (var variable in configuration.Variables)
|
|
||||||
result.Add(variable.Key, variable.Value);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetRuntimeVolume(ServerConfiguration configuration, AppConfiguration appConfiguration)
|
|
||||||
{
|
|
||||||
var localPath = PathBuilder.Dir(appConfiguration.Storage.Volumes, configuration.Id.ToString());
|
|
||||||
var absolutePath = Path.GetFullPath(localPath);
|
|
||||||
|
|
||||||
return absolutePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,87 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using MoonCore.Attributes;
|
using MoonCore.Attributes;
|
||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Models;
|
||||||
using MoonlightServers.Daemon.Configuration;
|
using MoonlightServers.Daemon.Configuration;
|
||||||
|
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.Services;
|
namespace MoonlightServers.Daemon.Services;
|
||||||
|
|
||||||
[Singleton]
|
[Singleton]
|
||||||
public class RemoteService
|
public class RemoteService
|
||||||
{
|
{
|
||||||
private readonly AppConfiguration Configuration;
|
private readonly HttpApiClient ApiClient;
|
||||||
|
|
||||||
public RemoteService(AppConfiguration configuration)
|
public RemoteService(AppConfiguration configuration)
|
||||||
{
|
{
|
||||||
Configuration = configuration;
|
ApiClient = CreateHttpClient(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<HttpApiClient> CreateHttpClient()
|
public async Task GetStatus()
|
||||||
{
|
{
|
||||||
var formattedUrl = Configuration.Remote.Url.EndsWith('/')
|
await ApiClient.Get("api/remote/servers/node/trip");
|
||||||
? Configuration.Remote.Url
|
}
|
||||||
: Configuration.Remote.Url + "/";
|
|
||||||
|
public async Task<PagedData<ServerDataResponse>> GetServers(int page, int perPage)
|
||||||
|
{
|
||||||
|
return await ApiClient.GetJson<PagedData<ServerDataResponse>>(
|
||||||
|
$"api/remote/servers?page={page}&pageSize={perPage}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerInstallDataResponse> GetServerInstallation(int serverId)
|
||||||
|
{
|
||||||
|
return await ApiClient.GetJson<ServerInstallDataResponse>(
|
||||||
|
$"api/remote/servers/{serverId}/install"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
private HttpApiClient CreateHttpClient(AppConfiguration configuration)
|
||||||
|
{
|
||||||
|
var formattedUrl = configuration.Remote.Url.EndsWith('/')
|
||||||
|
? configuration.Remote.Url
|
||||||
|
: configuration.Remote.Url + "/";
|
||||||
|
|
||||||
var httpClient = new HttpClient()
|
var httpClient = new HttpClient()
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(formattedUrl)
|
BaseAddress = new Uri(formattedUrl)
|
||||||
};
|
};
|
||||||
|
|
||||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Configuration.Security.Token}");
|
var jwt = GenerateJwt(configuration);
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
|
||||||
|
|
||||||
var apiClient = new HttpApiClient(httpClient);
|
return new HttpApiClient(httpClient);
|
||||||
|
|
||||||
return Task.FromResult(apiClient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GetStatus()
|
private string GenerateJwt(AppConfiguration configuration)
|
||||||
{
|
{
|
||||||
using var apiClient = await CreateHttpClient();
|
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||||
await apiClient.Get("api/servers/remote/node/trip");
|
|
||||||
|
var securityTokenDesc = new SecurityTokenDescriptor()
|
||||||
|
{
|
||||||
|
Expires = DateTime.UtcNow.AddYears(1), // TODO: Document somewhere
|
||||||
|
IssuedAt = DateTime.UtcNow,
|
||||||
|
Issuer = configuration.Security.TokenId,
|
||||||
|
Audience = configuration.Remote.Url,
|
||||||
|
NotBefore = DateTime.UtcNow.AddSeconds(-1),
|
||||||
|
SigningCredentials = new SigningCredentials(
|
||||||
|
new SymmetricSecurityKey(
|
||||||
|
Encoding.UTF8.GetBytes(configuration.Security.Token)
|
||||||
|
),
|
||||||
|
SecurityAlgorithms.HmacSha256
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
var securityToken = jwtSecurityTokenHandler.CreateJwtSecurityToken(securityTokenDesc);
|
||||||
|
|
||||||
|
securityToken.Header.Add("kid", configuration.Security.TokenId);
|
||||||
|
|
||||||
|
return jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -40,12 +40,9 @@ public class ServerService : IHostedLifecycleService
|
|||||||
|
|
||||||
// Loading models and converting them
|
// Loading models and converting them
|
||||||
Logger.LogInformation("Fetching servers from panel");
|
Logger.LogInformation("Fetching servers from panel");
|
||||||
using var apiClient = await RemoteService.CreateHttpClient();
|
|
||||||
|
|
||||||
var servers = await PagedData<ServerDataResponse>.All(async (page, pageSize) =>
|
var servers = await PagedData<ServerDataResponse>.All(async (page, pageSize) =>
|
||||||
await apiClient.GetJson<PagedData<ServerDataResponse>>(
|
await RemoteService.GetServers(page, pageSize)
|
||||||
$"api/servers/remote/servers?page={page}&pageSize={pageSize}"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var configurations = servers.Select(x => new ServerConfiguration()
|
var configurations = servers.Select(x => new ServerConfiguration()
|
||||||
|
|||||||
Reference in New Issue
Block a user