using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MoonCore.Common; using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; using MoonlightServers.ApiServer.Database.Entities; using MoonlightServers.DaemonShared.PanelSide.Http.Responses; namespace MoonlightServers.ApiServer.Http.Controllers.Remote; [ApiController] [Route("api/remote/servers")] [Authorize(AuthenticationSchemes = "nodeAuthentication")] public class ServersController : Controller { private readonly DatabaseRepository ServerRepository; private readonly DatabaseRepository NodeRepository; private readonly ILogger Logger; public ServersController( DatabaseRepository serverRepository, DatabaseRepository nodeRepository, ILogger logger ) { ServerRepository = serverRepository; NodeRepository = nodeRepository; Logger = logger; } [HttpGet] public async Task>> GetAsync( [FromQuery] int startIndex, [FromQuery] int count ) { if (count > 100) return Problem("Only 100 items can be fetched at a time", statusCode: 400); // Load the node via the id var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); var node = await NodeRepository .Get() .FirstAsync(x => x.Id == nodeId); var total = await ServerRepository .Get() .Where(x => x.Node.Id == node.Id) .CountAsync(); var servers = await ServerRepository .Get() .Where(x => x.Node.Id == node.Id) .Include(x => x.Star) .ThenInclude(x => x.DockerImages) .Include(x => x.Variables) .Include(x => x.Allocations) .Skip(startIndex) .Take(count) .ToArrayAsync(); var serverData = new List(); foreach (var server in servers) { var convertedData = ConvertToServerData(server); if (convertedData == null) continue; serverData.Add(convertedData); } return new CountedData() { Items = serverData.ToArray(), TotalCount = total }; } [HttpGet("{id:int}")] public async Task GetAsync([FromRoute] int id) { // Load the node via the id var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); var node = await NodeRepository .Get() .FirstAsync(x => x.Id == nodeId); // 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 .Get() .Where(x => x.Node.Id == node.Id) .Include(x => x.Star) .ThenInclude(x => x.DockerImages) .Include(x => x.Variables) .Include(x => x.Allocations) .FirstOrDefaultAsync(x => x.Id == id); if (server == null) throw new HttpApiException("No server with this id found", 404); var convertedData = ConvertToServerData(server); if (convertedData == null) throw new HttpApiException("An error occured while creating the server data model", 500); return convertedData; } [HttpGet("{id:int}/install")] public async Task GetInstallAsync([FromRoute] int id) { // Load the node via the id var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); var node = await NodeRepository .Get() .FirstAsync(x => x.Id == nodeId); // 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 .Get() .Where(x => x.Node.Id == node.Id) .Include(x => x.Star) .FirstOrDefaultAsync(x => x.Id == id); if (server == null) throw new HttpApiException("No server with this id found", 404); return new ServerInstallDataResponse() { Script = server.Star.InstallScript, DockerImage = server.Star.InstallDockerImage, Shell = server.Star.InstallShell }; } private ServerDataResponse? ConvertToServerData(Server server) { // Find the docker image to use for this server StarDockerImage? dockerImage = null; // Handle server set image if specified if (server.DockerImageIndex != -1) { dockerImage = server.Star.DockerImages .Skip(server.DockerImageIndex) .FirstOrDefault(); } // Handle star default image if set if (dockerImage == null && server.Star.DefaultDockerImage != -1) { dockerImage = server.Star.DockerImages .Skip(server.Star.DefaultDockerImage) .FirstOrDefault(); } if (dockerImage == null) dockerImage = server.Star.DockerImages.LastOrDefault(); if (dockerImage == null) { Logger.LogWarning("Unable to map server data for server {id}: No docker image available", server.Id); return null; } // Convert model return new ServerDataResponse() { Id = server.Id, StartupCommand = server.StartupOverride ?? server.Star.StartupCommand, Allocations = server.Allocations.Select(x => new AllocationDataResponse() { IpAddress = x.IpAddress, Port = x.Port }).ToArray(), Variables = server.Variables.ToDictionary(x => x.Key, x => x.Value), Cpu = server.Cpu, Disk = server.Disk, Memory = server.Memory, OnlineDetection = server.Star.OnlineDetection, DockerImage = dockerImage.Identifier, PullDockerImage = dockerImage.AutoPulling, ParseConiguration = server.Star.ParseConfiguration, StopCommand = server.Star.StopCommand, }; } }