Started reimplementing server feature because we use a new project structure now. This is way cleaner than the last implementation
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Services;
|
using Moonlight.Core.Services;
|
||||||
using Moonlight.Features.Community.Entities;
|
using Moonlight.Features.Community.Entities;
|
||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
using Moonlight.Features.ServiceManagement.Entities;
|
using Moonlight.Features.ServiceManagement.Entities;
|
||||||
using Moonlight.Features.StoreSystem.Entities;
|
using Moonlight.Features.StoreSystem.Entities;
|
||||||
using Moonlight.Features.Theming.Entities;
|
using Moonlight.Features.Theming.Entities;
|
||||||
@@ -39,6 +40,15 @@ public class DataContext : DbContext
|
|||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
public DbSet<Theme> Themes { get; set; }
|
public DbSet<Theme> Themes { get; set; }
|
||||||
|
|
||||||
|
// Servers
|
||||||
|
public DbSet<Server> Servers { get; set; }
|
||||||
|
public DbSet<ServerAllocation> ServerAllocations { get; set; }
|
||||||
|
public DbSet<ServerImage> ServerImages { get; set; }
|
||||||
|
public DbSet<ServerNode> ServerNodes { get; set; }
|
||||||
|
public DbSet<ServerVariable> ServerVariables { get; set; }
|
||||||
|
public DbSet<ServerDockerImage> ServerDockerImages { get; set; }
|
||||||
|
public DbSet<ServerImageVariable> ServerImageVariables { get; set; }
|
||||||
|
|
||||||
public DataContext(ConfigService configService)
|
public DataContext(ConfigService configService)
|
||||||
{
|
{
|
||||||
|
|||||||
99
Moonlight/Core/Helpers/WsPacketConnection.cs
Normal file
99
Moonlight/Core/Helpers/WsPacketConnection.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Helpers;
|
||||||
|
|
||||||
|
public class WsPacketConnection
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Type> Packets = new();
|
||||||
|
private readonly WebSocket WebSocket;
|
||||||
|
|
||||||
|
public WsPacketConnection(WebSocket webSocket)
|
||||||
|
{
|
||||||
|
WebSocket = webSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RegisterPacket<T>(string id)
|
||||||
|
{
|
||||||
|
lock (Packets)
|
||||||
|
Packets.Add(id, typeof(T));
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Send(object packet)
|
||||||
|
{
|
||||||
|
string? packetId = null;
|
||||||
|
|
||||||
|
// Search packet registration
|
||||||
|
lock (Packets)
|
||||||
|
{
|
||||||
|
if (Packets.Any(x => x.Value == packet.GetType()))
|
||||||
|
packetId = Packets.First(x => x.Value == packet.GetType()).Key;
|
||||||
|
|
||||||
|
if (packetId == null)
|
||||||
|
throw new ArgumentException($"A packet with the type {packet.GetType().FullName} is not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build raw packet
|
||||||
|
var rawPacket = new RawPacket()
|
||||||
|
{
|
||||||
|
Id = packetId,
|
||||||
|
Data = packet
|
||||||
|
};
|
||||||
|
|
||||||
|
// Serialize, encode and build buffer
|
||||||
|
var json = JsonConvert.SerializeObject(rawPacket);
|
||||||
|
var buffer = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
|
await WebSocket.SendAsync(buffer, WebSocketMessageType.Text, WebSocketMessageFlags.None,
|
||||||
|
CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<object?> Receive()
|
||||||
|
{
|
||||||
|
// Build buffer and read
|
||||||
|
var buffer = new byte[1024];
|
||||||
|
await WebSocket.ReceiveAsync(buffer, CancellationToken.None);
|
||||||
|
|
||||||
|
// Decode and deserialize
|
||||||
|
var json = Encoding.UTF8.GetString(buffer);
|
||||||
|
var rawPacket = JsonConvert.DeserializeObject<RawPacket>(json)!;
|
||||||
|
|
||||||
|
object? packetType = null;
|
||||||
|
|
||||||
|
// Search packet registration
|
||||||
|
lock (Packets)
|
||||||
|
{
|
||||||
|
if (Packets.ContainsKey(rawPacket.Id))
|
||||||
|
packetType = Packets[rawPacket.Id];
|
||||||
|
|
||||||
|
if (packetType == null)
|
||||||
|
throw new ArgumentException($"A packet with the type {rawPacket.Id} is not registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
var typedPacketType = typeof(RawPacket<>).MakeGenericType((packetType as Type)!);
|
||||||
|
var typedPacket = JsonConvert.DeserializeObject(json, typedPacketType);
|
||||||
|
|
||||||
|
return typedPacketType.GetProperty("Data")!.GetValue(typedPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Close()
|
||||||
|
{
|
||||||
|
if(WebSocket.State == WebSocketState.Open)
|
||||||
|
await WebSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RawPacket
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public object Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RawPacket<T>
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public T Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Moonlight/Features/Servers/Entities/Server.cs
Normal file
24
Moonlight/Features/Servers/Entities/Server.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Moonlight.Features.ServiceManagement.Entities;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class Server
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Service Service { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int Cpu { get; set; }
|
||||||
|
public int Memory { get; set; }
|
||||||
|
public int Disk { get; set; }
|
||||||
|
|
||||||
|
public ServerImage Image { get; set; }
|
||||||
|
public int DockerImageIndex { get; set; }
|
||||||
|
public string? OverrideStartupCommand { get; set; }
|
||||||
|
public List<ServerVariable> Variables { get; set; }
|
||||||
|
|
||||||
|
public ServerNode Node { get; set; }
|
||||||
|
public ServerAllocation MainAllocation { get; set; }
|
||||||
|
public List<ServerAllocation> Allocations { get; set; } = new();
|
||||||
|
}
|
||||||
8
Moonlight/Features/Servers/Entities/ServerAllocation.cs
Normal file
8
Moonlight/Features/Servers/Entities/ServerAllocation.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerAllocation
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string IpAddress { get; set; } = "0.0.0.0";
|
||||||
|
public int Port { get; set; }
|
||||||
|
}
|
||||||
9
Moonlight/Features/Servers/Entities/ServerDockerImage.cs
Normal file
9
Moonlight/Features/Servers/Entities/ServerDockerImage.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerDockerImage
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public bool AutoPull { get; set; } = true;
|
||||||
|
}
|
||||||
24
Moonlight/Features/Servers/Entities/ServerImage.cs
Normal file
24
Moonlight/Features/Servers/Entities/ServerImage.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerImage
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int AllocationsNeeded { get; set; }
|
||||||
|
public string StartupCommand { get; set; }
|
||||||
|
public string StopCommand { get; set; }
|
||||||
|
public string OnlineDetection { get; set; }
|
||||||
|
public string ParseConfigurations { get; set; } = "[]";
|
||||||
|
|
||||||
|
public string InstallDockerImage { get; set; }
|
||||||
|
public string InstallShell { get; set; }
|
||||||
|
public string InstallScript { get; set; }
|
||||||
|
|
||||||
|
public string Author { get; set; }
|
||||||
|
public string? DonateUrl { get; set; }
|
||||||
|
public string? UpdateUrl { get; set; }
|
||||||
|
|
||||||
|
public List<ServerImageVariable> Variables = new();
|
||||||
|
public List<ServerDockerImage> DockerImages { get; set; }
|
||||||
|
}
|
||||||
13
Moonlight/Features/Servers/Entities/ServerImageVariable.cs
Normal file
13
Moonlight/Features/Servers/Entities/ServerImageVariable.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerImageVariable
|
||||||
|
{
|
||||||
|
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 AllowUserToEdit { get; set; }
|
||||||
|
public bool AllowUserToView { get; set; }
|
||||||
|
}
|
||||||
13
Moonlight/Features/Servers/Entities/ServerNode.cs
Normal file
13
Moonlight/Features/Servers/Entities/ServerNode.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerNode
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Token { get; set; }
|
||||||
|
public int HttpPort { get; set; }
|
||||||
|
public int FtpPort { get; set; }
|
||||||
|
|
||||||
|
public List<ServerAllocation> Allocations { get; set; } = new();
|
||||||
|
}
|
||||||
8
Moonlight/Features/Servers/Entities/ServerVariable.cs
Normal file
8
Moonlight/Features/Servers/Entities/ServerVariable.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Entities;
|
||||||
|
|
||||||
|
public class ServerVariable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Extensions.Attributes;
|
||||||
|
|
||||||
|
public class EnableNodeMiddlewareAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
63
Moonlight/Features/Servers/Extensions/ServerExtensions.cs
Normal file
63
Moonlight/Features/Servers/Extensions/ServerExtensions.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
|
using Moonlight.Features.Servers.Models.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Extensions;
|
||||||
|
|
||||||
|
public static class ServerExtensions
|
||||||
|
{
|
||||||
|
public static ServerConfiguration ToServerConfiguration(this Server server)
|
||||||
|
{
|
||||||
|
var serverConfiguration = new ServerConfiguration();
|
||||||
|
|
||||||
|
// Set general information
|
||||||
|
serverConfiguration.Id = server.Id;
|
||||||
|
|
||||||
|
// Set variables
|
||||||
|
serverConfiguration.Variables = server.Variables
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
|
// Set server image
|
||||||
|
serverConfiguration.Image = new()
|
||||||
|
{
|
||||||
|
OnlineDetection = server.Image.OnlineDetection,
|
||||||
|
ParseConfigurations = server.Image.ParseConfigurations,
|
||||||
|
StartupCommand = server.Image.StartupCommand,
|
||||||
|
StopCommand = server.Image.StopCommand
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find docker image by index
|
||||||
|
ServerDockerImage dockerImage;
|
||||||
|
|
||||||
|
if (server.DockerImageIndex >= server.Image.DockerImages.Count || server.DockerImageIndex == -1)
|
||||||
|
dockerImage = server.Image.DockerImages.Last();
|
||||||
|
else
|
||||||
|
dockerImage = server.Image.DockerImages[server.DockerImageIndex];
|
||||||
|
|
||||||
|
serverConfiguration.Image.DockerImage = dockerImage.Name;
|
||||||
|
serverConfiguration.Image.PullDockerImage = dockerImage.AutoPull;
|
||||||
|
|
||||||
|
// Set server limits
|
||||||
|
serverConfiguration.Limits = new()
|
||||||
|
{
|
||||||
|
Cpu = server.Cpu,
|
||||||
|
Memory = server.Memory,
|
||||||
|
Disk = server.Disk
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set allocations
|
||||||
|
serverConfiguration.Allocations = server.Allocations.Select(x => new ServerConfiguration.AllocationData()
|
||||||
|
{
|
||||||
|
IpAddress = x.IpAddress,
|
||||||
|
Port = x.Port
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
// Set main allocation
|
||||||
|
serverConfiguration.MainAllocation = new()
|
||||||
|
{
|
||||||
|
IpAddress = server.MainAllocation.IpAddress,
|
||||||
|
Port = server.MainAllocation.Port
|
||||||
|
};
|
||||||
|
|
||||||
|
return serverConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
|
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||||
|
using Moonlight.Features.Servers.Services;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Http.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/servers/node")]
|
||||||
|
[EnableNodeMiddleware]
|
||||||
|
public class NodeController : Controller
|
||||||
|
{
|
||||||
|
private readonly NodeService NodeService;
|
||||||
|
|
||||||
|
public NodeController(NodeService nodeService)
|
||||||
|
{
|
||||||
|
NodeService = nodeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("notify/start")]
|
||||||
|
public async Task<ActionResult> NotifyBootStart()
|
||||||
|
{
|
||||||
|
// Load node from request context
|
||||||
|
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||||
|
|
||||||
|
await NodeService.UpdateMeta(node, meta =>
|
||||||
|
{
|
||||||
|
meta.IsBooting = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("notify/finish")]
|
||||||
|
public async Task<ActionResult> NotifyBootFinish()
|
||||||
|
{
|
||||||
|
// Load node from request context
|
||||||
|
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||||
|
|
||||||
|
await NodeService.UpdateMeta(node, meta =>
|
||||||
|
{
|
||||||
|
meta.IsBooting = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.Core.Helpers;
|
||||||
|
using Moonlight.Core.Repositories;
|
||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
|
using Moonlight.Features.Servers.Extensions;
|
||||||
|
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||||
|
using Moonlight.Features.Servers.Models.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Http.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/servers")]
|
||||||
|
[EnableNodeMiddleware]
|
||||||
|
public class ServersControllers : Controller
|
||||||
|
{
|
||||||
|
private readonly Repository<Server> ServerRepository;
|
||||||
|
|
||||||
|
public ServersControllers(Repository<Server> serverRepository)
|
||||||
|
{
|
||||||
|
ServerRepository = serverRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("ws")]
|
||||||
|
public async Task<ActionResult> GetAllServersWs()
|
||||||
|
{
|
||||||
|
// Validate if it is even a websocket connection
|
||||||
|
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
|
return BadRequest("This endpoint is only available for websockets");
|
||||||
|
|
||||||
|
// Accept websocket connection
|
||||||
|
var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||||
|
|
||||||
|
// Build connection wrapper
|
||||||
|
var wsPacketConnection = new WsPacketConnection(websocket);
|
||||||
|
await wsPacketConnection.RegisterPacket<ServerConfiguration>("serverConfiguration");
|
||||||
|
|
||||||
|
// Read server data for the node
|
||||||
|
var node = (HttpContext.Items["Node"] as ServerNode)!;
|
||||||
|
|
||||||
|
// Load server data with including the relational data
|
||||||
|
var servers = ServerRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Allocations)
|
||||||
|
.Include(x => x.MainAllocation)
|
||||||
|
.Include(x => x.Image)
|
||||||
|
.ThenInclude(x => x.Variables)
|
||||||
|
.Include(x => x.Image)
|
||||||
|
.ThenInclude(x => x.DockerImages)
|
||||||
|
.Where(x => x.Node.Id == node.Id)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
// Convert the data to server configurations
|
||||||
|
var serverConfigurations = servers
|
||||||
|
.Select(x => x.ToServerConfiguration())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
// Send the server configurations
|
||||||
|
foreach (var serverConfiguration in serverConfigurations)
|
||||||
|
await wsPacketConnection.Send(serverConfiguration);
|
||||||
|
|
||||||
|
// Close the connection
|
||||||
|
await wsPacketConnection.Close();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
88
Moonlight/Features/Servers/Http/Middleware/NodeMiddleware.cs
Normal file
88
Moonlight/Features/Servers/Http/Middleware/NodeMiddleware.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Moonlight.Core.Repositories;
|
||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
|
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Http.Middleware;
|
||||||
|
|
||||||
|
public class NodeMiddleware
|
||||||
|
{
|
||||||
|
private RequestDelegate Next;
|
||||||
|
private readonly Repository<ServerNode> NodeRepository;
|
||||||
|
|
||||||
|
public NodeMiddleware(RequestDelegate next, Repository<ServerNode> nodeRepository)
|
||||||
|
{
|
||||||
|
Next = next;
|
||||||
|
NodeRepository = nodeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check if the path is targeting the /api/servers endpoints
|
||||||
|
if (!context.Request.Path.HasValue || !context.Request.Path.Value.StartsWith("/api/servers"))
|
||||||
|
{
|
||||||
|
await Next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load endpoint
|
||||||
|
var endpoint = context.Features.Get<IEndpointFeature>();
|
||||||
|
|
||||||
|
// Null checks to ensure we have data to check
|
||||||
|
if (endpoint == null || endpoint.Endpoint == null)
|
||||||
|
{
|
||||||
|
await Next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference to the controller meta
|
||||||
|
var controllerMeta = endpoint.Endpoint.Metadata;
|
||||||
|
|
||||||
|
// If the node middleware attribute is missing, we want to continue
|
||||||
|
if(controllerMeta.All(x => x is EnableNodeMiddlewareAttribute))
|
||||||
|
{
|
||||||
|
await Next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we actually want to validate the request
|
||||||
|
// so every return after this text will prevent
|
||||||
|
// the call of the controller action
|
||||||
|
|
||||||
|
// Check if header exists
|
||||||
|
if (!context.Request.Headers.ContainsKey("Authorization"))
|
||||||
|
{
|
||||||
|
// TODO: Add a proper extensions pack to support proper error messages
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = context.Request.Headers["Authorization"];
|
||||||
|
|
||||||
|
// Check if header is null
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any node has the token specified by the request
|
||||||
|
var node = NodeRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Token == token);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request is valid, because we found a node by this token
|
||||||
|
// so now we want to save it for the controller to use and
|
||||||
|
// continue in the request pipeline
|
||||||
|
|
||||||
|
context.Items["Node"] = node;
|
||||||
|
|
||||||
|
await Next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Models.Abstractions;
|
||||||
|
|
||||||
|
public class NodeMeta
|
||||||
|
{
|
||||||
|
public bool IsBooting { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
namespace Moonlight.Features.Servers.Models.Abstractions;
|
||||||
|
|
||||||
|
public class ServerConfiguration
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public LimitsData Limits { get; set; }
|
||||||
|
public ImageData Image { get; set; }
|
||||||
|
public AllocationData MainAllocation { get; set; }
|
||||||
|
public List<AllocationData> Allocations { get; set; }
|
||||||
|
public Dictionary<string, string> Variables { get; set; } = new();
|
||||||
|
|
||||||
|
public class LimitsData
|
||||||
|
{
|
||||||
|
public int Cpu { get; set; }
|
||||||
|
public int Memory { get; set; }
|
||||||
|
public int Disk { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ImageData
|
||||||
|
{
|
||||||
|
public string DockerImage { get; set; }
|
||||||
|
public bool PullDockerImage { get; set; }
|
||||||
|
public string StartupCommand { get; set; }
|
||||||
|
public string StopCommand { get; set; }
|
||||||
|
public string OnlineDetection { get; set; }
|
||||||
|
public string ParseConfigurations { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AllocationData
|
||||||
|
{
|
||||||
|
public string IpAddress { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Moonlight/Features/Servers/Services/NodeService.cs
Normal file
30
Moonlight/Features/Servers/Services/NodeService.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
|
using Moonlight.Features.Servers.Models.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.Servers.Services;
|
||||||
|
|
||||||
|
public class NodeService
|
||||||
|
{
|
||||||
|
private readonly Dictionary<int, NodeMeta> MetaCache = new();
|
||||||
|
|
||||||
|
public Task UpdateMeta(ServerNode node, Action<NodeMeta> metaAction)
|
||||||
|
{
|
||||||
|
lock (MetaCache)
|
||||||
|
{
|
||||||
|
NodeMeta? meta = null;
|
||||||
|
|
||||||
|
if (MetaCache.ContainsKey(node.Id))
|
||||||
|
meta = MetaCache[node.Id];
|
||||||
|
|
||||||
|
if (meta == null)
|
||||||
|
{
|
||||||
|
meta = new();
|
||||||
|
MetaCache.Add(node.Id, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
metaAction.Invoke(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,14 @@
|
|||||||
<Folder Include="Features\Dummy\Services\" />
|
<Folder Include="Features\Dummy\Services\" />
|
||||||
<Folder Include="Features\Dummy\UI\Components\" />
|
<Folder Include="Features\Dummy\UI\Components\" />
|
||||||
<Folder Include="Features\Dummy\UI\Views\" />
|
<Folder Include="Features\Dummy\UI\Views\" />
|
||||||
|
<Folder Include="Features\Servers\Configuration\" />
|
||||||
|
<Folder Include="Features\Servers\Exceptions\" />
|
||||||
|
<Folder Include="Features\Servers\Helpers\" />
|
||||||
|
<Folder Include="Features\Servers\Http\Requests\" />
|
||||||
|
<Folder Include="Features\Servers\Http\Resources\" />
|
||||||
|
<Folder Include="Features\Servers\Models\Forms\" />
|
||||||
|
<Folder Include="Features\Servers\UI\Components\" />
|
||||||
|
<Folder Include="Features\Servers\UI\Views\" />
|
||||||
<Folder Include="Features\StoreSystem\Helpers\" />
|
<Folder Include="Features\StoreSystem\Helpers\" />
|
||||||
<Folder Include="Features\Ticketing\Models\Abstractions\" />
|
<Folder Include="Features\Ticketing\Models\Abstractions\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ using Moonlight.Core.Services.Users;
|
|||||||
using Moonlight.Core.Services.Utils;
|
using Moonlight.Core.Services.Utils;
|
||||||
using Moonlight.Features.Advertisement.Services;
|
using Moonlight.Features.Advertisement.Services;
|
||||||
using Moonlight.Features.Community.Services;
|
using Moonlight.Features.Community.Services;
|
||||||
|
using Moonlight.Features.Servers.Http.Middleware;
|
||||||
|
using Moonlight.Features.Servers.Services;
|
||||||
using Moonlight.Features.ServiceManagement.Entities.Enums;
|
using Moonlight.Features.ServiceManagement.Entities.Enums;
|
||||||
using Moonlight.Features.ServiceManagement.Services;
|
using Moonlight.Features.ServiceManagement.Services;
|
||||||
using Moonlight.Features.StoreSystem.Services;
|
using Moonlight.Features.StoreSystem.Services;
|
||||||
@@ -51,6 +53,10 @@ builder.Services.AddSingleton(pluginService);
|
|||||||
await pluginService.Load(builder);
|
await pluginService.Load(builder);
|
||||||
await pluginService.RunPreInit();
|
await pluginService.RunPreInit();
|
||||||
|
|
||||||
|
// TODO: Add automatic assembly scanning
|
||||||
|
// dependency injection registration
|
||||||
|
// using attributes
|
||||||
|
|
||||||
builder.Services.AddDbContext<DataContext>();
|
builder.Services.AddDbContext<DataContext>();
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
@@ -99,6 +105,9 @@ builder.Services.AddScoped<TicketService>();
|
|||||||
builder.Services.AddScoped<TicketChatService>();
|
builder.Services.AddScoped<TicketChatService>();
|
||||||
builder.Services.AddScoped<TicketCreateService>();
|
builder.Services.AddScoped<TicketCreateService>();
|
||||||
|
|
||||||
|
// Services / Servers
|
||||||
|
builder.Services.AddSingleton<NodeService>();
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
builder.Services.AddScoped<IdentityService>();
|
builder.Services.AddScoped<IdentityService>();
|
||||||
builder.Services.AddSingleton(configService);
|
builder.Services.AddSingleton(configService);
|
||||||
@@ -127,6 +136,8 @@ var app = builder.Build();
|
|||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseMiddleware<NodeMiddleware>();
|
||||||
|
|
||||||
app.MapBlazorHub();
|
app.MapBlazorHub();
|
||||||
app.MapFallbackToPage("/_Host");
|
app.MapFallbackToPage("/_Host");
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|||||||
Reference in New Issue
Block a user