Added node server sync and delete sync. Cleaned up codebase and extracted calls to apis to services
This commit is contained in:
@@ -8,6 +8,7 @@ using MoonCore.Helpers;
|
|||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
|
using MoonlightServers.ApiServer.Services;
|
||||||
using MoonlightServers.Shared.Http.Requests.Admin.Servers;
|
using MoonlightServers.Shared.Http.Requests.Admin.Servers;
|
||||||
using MoonlightServers.Shared.Http.Responses.Admin.Servers;
|
using MoonlightServers.Shared.Http.Responses.Admin.Servers;
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ public class ServersController : Controller
|
|||||||
private readonly DatabaseRepository<ServerVariable> VariableRepository;
|
private readonly DatabaseRepository<ServerVariable> VariableRepository;
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
private readonly DatabaseRepository<Server> ServerRepository;
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
private readonly DatabaseRepository<User> UserRepository;
|
||||||
|
private readonly ILogger<ServersController> Logger;
|
||||||
|
private readonly ServerService ServerService;
|
||||||
|
|
||||||
public ServersController(
|
public ServersController(
|
||||||
CrudHelper<Server, ServerDetailResponse> crudHelper,
|
CrudHelper<Server, ServerDetailResponse> crudHelper,
|
||||||
@@ -32,7 +35,10 @@ public class ServersController : Controller
|
|||||||
DatabaseRepository<Allocation> allocationRepository,
|
DatabaseRepository<Allocation> allocationRepository,
|
||||||
DatabaseRepository<ServerVariable> variableRepository,
|
DatabaseRepository<ServerVariable> variableRepository,
|
||||||
DatabaseRepository<Server> serverRepository,
|
DatabaseRepository<Server> serverRepository,
|
||||||
DatabaseRepository<User> userRepository)
|
DatabaseRepository<User> userRepository,
|
||||||
|
ILogger<ServersController> logger,
|
||||||
|
ServerService serverService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
CrudHelper = crudHelper;
|
CrudHelper = crudHelper;
|
||||||
StarRepository = starRepository;
|
StarRepository = starRepository;
|
||||||
@@ -41,6 +47,8 @@ public class ServersController : Controller
|
|||||||
VariableRepository = variableRepository;
|
VariableRepository = variableRepository;
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
|
ServerService = serverService;
|
||||||
|
Logger = logger;
|
||||||
|
|
||||||
CrudHelper.QueryModifier = servers => servers
|
CrudHelper.QueryModifier = servers => servers
|
||||||
.Include(x => x.Node)
|
.Include(x => x.Node)
|
||||||
@@ -162,10 +170,23 @@ public class ServersController : Controller
|
|||||||
server.Node = node;
|
server.Node = node;
|
||||||
server.Star = star;
|
server.Star = star;
|
||||||
|
|
||||||
// TODO: Call node
|
|
||||||
|
|
||||||
var finalServer = await ServerRepository.Add(server);
|
var finalServer = await ServerRepository.Add(server);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ServerService.Sync(finalServer);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError("Unable to sync server to node the server is assigned to: {e}", e);
|
||||||
|
|
||||||
|
// We are deleting the server from the database after the creation has failed
|
||||||
|
// to ensure we wont have a bugged server in the database which doesnt exist on the node
|
||||||
|
await ServerRepository.Remove(finalServer);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalServer);
|
return CrudHelper.MapToResult(finalServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,23 +236,45 @@ public class ServersController : Controller
|
|||||||
var serverVar = server.Variables
|
var serverVar = server.Variables
|
||||||
.FirstOrDefault(x => x.Key == variable.Key);
|
.FirstOrDefault(x => x.Key == variable.Key);
|
||||||
|
|
||||||
if(serverVar == null)
|
if (serverVar == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Update value
|
// Update value
|
||||||
serverVar.Value = variable.Value;
|
serverVar.Value = variable.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Call node
|
|
||||||
|
|
||||||
await ServerRepository.Update(server);
|
await ServerRepository.Update(server);
|
||||||
|
|
||||||
|
// Notify the node about the changes
|
||||||
|
await ServerService.Sync(server);
|
||||||
|
|
||||||
return CrudHelper.MapToResult(server);
|
return CrudHelper.MapToResult(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
[HttpDelete("{id:int}")]
|
||||||
public async Task Delete([FromRoute] int id)
|
public async Task Delete([FromRoute] int id, [FromQuery] bool force = false)
|
||||||
{
|
{
|
||||||
|
var server = await CrudHelper.GetSingleModel(id);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If the sync fails on the node and we aren't forcing the deletion,
|
||||||
|
// we don't want to delete it from the database yet
|
||||||
|
await ServerService.SyncDelete(server);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (force)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(
|
||||||
|
"An error occured while syncing deletion of a server to the node. Continuing anyways. Error: {e}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
await CrudHelper.Delete(id);
|
await CrudHelper.Delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonCore.Extended.Abstractions;
|
using MoonCore.Extended.Abstractions;
|
||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Services;
|
using MoonlightServers.ApiServer.Services;
|
||||||
|
|
||||||
@@ -15,113 +16,69 @@ namespace MoonlightServers.ApiServer.Http.Controllers.Client;
|
|||||||
public class ServerPowerController : Controller
|
public class ServerPowerController : Controller
|
||||||
{
|
{
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
private readonly DatabaseRepository<Server> ServerRepository;
|
||||||
private readonly NodeService NodeService;
|
private readonly DatabaseRepository<User> UserRepository;
|
||||||
|
private readonly ServerService ServerService;
|
||||||
|
|
||||||
public ServerPowerController(DatabaseRepository<Server> serverRepository, NodeService nodeService)
|
public ServerPowerController(
|
||||||
|
DatabaseRepository<Server> serverRepository,
|
||||||
|
DatabaseRepository<User> userRepository,
|
||||||
|
ServerService serverService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
NodeService = nodeService;
|
UserRepository = userRepository;
|
||||||
|
ServerService = serverService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/start")]
|
[HttpPost("{serverId:int}/start")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task Start([FromRoute] int serverId)
|
public async Task Start([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
await ServerService.Start(server);
|
||||||
using var apiClient = await NodeService.CreateApiClient(server.Node);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await apiClient.Post($"api/servers/{server.Id}/start");
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/stop")]
|
[HttpPost("{serverId:int}/stop")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task Stop([FromRoute] int serverId)
|
public async Task Stop([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
await ServerService.Stop(server);
|
||||||
using var apiClient = await NodeService.CreateApiClient(server.Node);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await apiClient.Post($"api/servers/{server.Id}/stop");
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/kill")]
|
[HttpPost("{serverId:int}/kill")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task Kill([FromRoute] int serverId)
|
public async Task Kill([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
await ServerService.Kill(server);
|
||||||
using var apiClient = await NodeService.CreateApiClient(server.Node);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await apiClient.Post($"api/servers/{server.Id}/kill");
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/install")]
|
[HttpPost("{serverId:int}/install")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task Install([FromRoute] int serverId)
|
public async Task Install([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
await ServerService.Install(server);
|
||||||
using var apiClient = await NodeService.CreateApiClient(server.Node);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await apiClient.Post($"api/servers/{server.Id}/install");
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Server> GetServerWithPermCheck(int serverId,
|
private async Task<Server> GetServerById(int serverId)
|
||||||
Func<IQueryable<Server>, IQueryable<Server>>? queryModifier = null)
|
|
||||||
{
|
{
|
||||||
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
var server = await ServerRepository
|
||||||
var userId = int.Parse(userIdClaim.Value);
|
|
||||||
|
|
||||||
var query = ServerRepository
|
|
||||||
.Get()
|
.Get()
|
||||||
.Include(x => x.Node) as IQueryable<Server>;
|
.Include(x => x.Node)
|
||||||
|
|
||||||
if (queryModifier != null)
|
|
||||||
query = queryModifier.Invoke(query);
|
|
||||||
|
|
||||||
var server = await query
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
.FirstOrDefaultAsync(x => x.Id == serverId);
|
||||||
|
|
||||||
if (server == null)
|
if (server == null)
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
if (server.OwnerId == userId) // The current user is the owner
|
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
||||||
return server;
|
var userId = int.Parse(userIdClaim.Value);
|
||||||
|
var user = await UserRepository.Get().FirstAsync(x => x.Id == userId);
|
||||||
|
|
||||||
var permissions = User.Claims.First(x => x.Type == "permissions").Value.Split(";", StringSplitOptions.RemoveEmptyEntries);
|
if (!ServerService.IsAllowedToAccess(user, server))
|
||||||
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
if (PermissionHelper.HasPermission(permissions, "admin.servers.get")) // The current user is an admin
|
return server;
|
||||||
return server;
|
|
||||||
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonCore.Extended.Abstractions;
|
using MoonCore.Extended.Abstractions;
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Extensions;
|
using MoonlightServers.ApiServer.Extensions;
|
||||||
using MoonlightServers.ApiServer.Services;
|
using MoonlightServers.ApiServer.Services;
|
||||||
@@ -17,13 +17,17 @@ namespace MoonlightServers.ApiServer.Http.Controllers.Client;
|
|||||||
[Route("api/client/servers")]
|
[Route("api/client/servers")]
|
||||||
public class ServersController : Controller
|
public class ServersController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly ServerService ServerService;
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
private readonly DatabaseRepository<Server> ServerRepository;
|
||||||
|
private readonly DatabaseRepository<User> UserRepository;
|
||||||
private readonly NodeService NodeService;
|
private readonly NodeService NodeService;
|
||||||
|
|
||||||
public ServersController(DatabaseRepository<Server> serverRepository, NodeService nodeService)
|
public ServersController(DatabaseRepository<Server> serverRepository, NodeService nodeService, ServerService serverService, DatabaseRepository<User> userRepository)
|
||||||
{
|
{
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
NodeService = nodeService;
|
NodeService = nodeService;
|
||||||
|
ServerService = serverService;
|
||||||
|
UserRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -71,13 +75,22 @@ public class ServersController : Controller
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ServerDetailResponse> Get([FromRoute] int serverId)
|
public async Task<ServerDetailResponse> Get([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(
|
var server = await ServerRepository
|
||||||
serverId,
|
.Get()
|
||||||
query =>
|
.Include(x => x.Allocations)
|
||||||
query
|
.Include(x => x.Star)
|
||||||
.Include(x => x.Allocations)
|
.Include(x => x.Node)
|
||||||
.Include(x => x.Star)
|
.FirstOrDefaultAsync(x => x.Id == serverId);
|
||||||
);
|
|
||||||
|
if(server == null)
|
||||||
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
|
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
||||||
|
var userId = int.Parse(userIdClaim.Value);
|
||||||
|
var user = await UserRepository.Get().FirstAsync(x => x.Id == userId);
|
||||||
|
|
||||||
|
if(!ServerService.IsAllowedToAccess(user, server))
|
||||||
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
return new ServerDetailResponse()
|
return new ServerDetailResponse()
|
||||||
{
|
{
|
||||||
@@ -98,32 +111,20 @@ public class ServersController : Controller
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
public async Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
var status = await ServerService.GetStatus(server);
|
||||||
|
|
||||||
var apiClient = await NodeService.CreateApiClient(server.Node);
|
return new ServerStatusResponse()
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var data = await apiClient.GetJson<DaemonShared.DaemonSide.Http.Responses.Servers.ServerStatusResponse>(
|
State = status.State.ToServerPowerState()
|
||||||
$"api/servers/{server.Id}/status"
|
};
|
||||||
);
|
|
||||||
|
|
||||||
return new ServerStatusResponse()
|
|
||||||
{
|
|
||||||
State = data.State.ToServerPowerState()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/ws")]
|
[HttpGet("{serverId:int}/ws")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ServerWebSocketResponse> GetWebSocket([FromRoute] int serverId)
|
public async Task<ServerWebSocketResponse> GetWebSocket([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
|
||||||
// TODO: Handle transparent node proxy
|
// TODO: Handle transparent node proxy
|
||||||
|
|
||||||
@@ -135,11 +136,7 @@ public class ServersController : Controller
|
|||||||
|
|
||||||
var url = "";
|
var url = "";
|
||||||
|
|
||||||
if (server.Node.UseSsl)
|
url += server.Node.UseSsl ? "https://" : "http://";
|
||||||
url += "https://";
|
|
||||||
else
|
|
||||||
url += "http://";
|
|
||||||
|
|
||||||
url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/api/servers/ws";
|
url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/api/servers/ws";
|
||||||
|
|
||||||
return new ServerWebSocketResponse()
|
return new ServerWebSocketResponse()
|
||||||
@@ -153,54 +150,33 @@ public class ServersController : Controller
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ServerLogsResponse> GetLogs([FromRoute] int serverId)
|
public async Task<ServerLogsResponse> GetLogs([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = await GetServerWithPermCheck(serverId);
|
var server = await GetServerById(serverId);
|
||||||
|
|
||||||
var apiClient = await NodeService.CreateApiClient(server.Node);
|
var logs = await ServerService.GetLogs(server);
|
||||||
|
|
||||||
try
|
return new ServerLogsResponse()
|
||||||
{
|
{
|
||||||
var data = await apiClient.GetJson<DaemonShared.DaemonSide.Http.Responses.Servers.ServerLogsResponse>(
|
Messages = logs.Messages
|
||||||
$"api/servers/{server.Id}/logs"
|
};
|
||||||
);
|
|
||||||
|
|
||||||
return new ServerLogsResponse()
|
|
||||||
{
|
|
||||||
Messages = data.Messages
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (HttpRequestException e)
|
|
||||||
{
|
|
||||||
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Server> GetServerWithPermCheck(int serverId,
|
private async Task<Server> GetServerById(int serverId)
|
||||||
Func<IQueryable<Server>, IQueryable<Server>>? queryModifier = null)
|
|
||||||
{
|
{
|
||||||
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
var server = await ServerRepository
|
||||||
var userId = int.Parse(userIdClaim.Value);
|
|
||||||
|
|
||||||
var query = ServerRepository
|
|
||||||
.Get()
|
.Get()
|
||||||
.Include(x => x.Node) as IQueryable<Server>;
|
.Include(x => x.Node)
|
||||||
|
|
||||||
if (queryModifier != null)
|
|
||||||
query = queryModifier.Invoke(query);
|
|
||||||
|
|
||||||
var server = await query
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
.FirstOrDefaultAsync(x => x.Id == serverId);
|
||||||
|
|
||||||
if (server == null)
|
if(server == null)
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
if (server.OwnerId == userId) // The current user is the owner
|
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
||||||
return server;
|
var userId = int.Parse(userIdClaim.Value);
|
||||||
|
var user = await UserRepository.Get().FirstAsync(x => x.Id == userId);
|
||||||
|
|
||||||
var permissions = User.Claims.First(x => x.Type == "permissions").Value.Split(";", StringSplitOptions.RemoveEmptyEntries);
|
if(!ServerService.IsAllowedToAccess(user, server))
|
||||||
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
if (PermissionHelper.HasPermission(permissions, "admin.servers.get")) // The current user is an admin
|
return server;
|
||||||
return server;
|
|
||||||
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,47 +58,12 @@ public class ServersController : Controller
|
|||||||
|
|
||||||
foreach (var server in servers)
|
foreach (var server in servers)
|
||||||
{
|
{
|
||||||
var dockerImage = server.Star.DockerImages
|
var convertedData = ConvertToServerData(server);
|
||||||
.Skip(server.DockerImageIndex)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (dockerImage == null)
|
if (convertedData == null)
|
||||||
{
|
|
||||||
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);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
serverData.Add(new ServerDataResponse()
|
serverData.Add(convertedData);
|
||||||
{
|
|
||||||
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),
|
|
||||||
Bandwidth = server.Bandwidth,
|
|
||||||
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,
|
|
||||||
UseVirtualDisk = server.UseVirtualDisk
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PagedData<ServerDataResponse>()
|
return new PagedData<ServerDataResponse>()
|
||||||
@@ -111,6 +76,38 @@ public class ServersController : Controller
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}")]
|
||||||
|
public async Task<ServerDataResponse> Get([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
|
||||||
|
.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")]
|
[HttpGet("{id:int}/install")]
|
||||||
public async Task<ServerInstallDataResponse> GetInstall([FromRoute] int id)
|
public async Task<ServerInstallDataResponse> GetInstall([FromRoute] int id)
|
||||||
{
|
{
|
||||||
@@ -139,4 +136,58 @@ public class ServersController : Controller
|
|||||||
Shell = server.Star.InstallShell
|
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),
|
||||||
|
Bandwidth = server.Bandwidth,
|
||||||
|
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,
|
||||||
|
UseVirtualDisk = server.UseVirtualDisk
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,27 +14,6 @@ namespace MoonlightServers.ApiServer.Services;
|
|||||||
[Singleton]
|
[Singleton]
|
||||||
public class NodeService
|
public class NodeService
|
||||||
{
|
{
|
||||||
public async Task<HttpApiClient> CreateApiClient(Node node)
|
|
||||||
{
|
|
||||||
var url = "";
|
|
||||||
|
|
||||||
if (node.UseSsl)
|
|
||||||
url += "https://";
|
|
||||||
else
|
|
||||||
url += "http://";
|
|
||||||
|
|
||||||
url += $"{node.Fqdn}:{node.HttpPort}/";
|
|
||||||
|
|
||||||
var httpClient = new HttpClient()
|
|
||||||
{
|
|
||||||
BaseAddress = new Uri(url)
|
|
||||||
};
|
|
||||||
|
|
||||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {node.Token}");
|
|
||||||
|
|
||||||
return new HttpApiClient(httpClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CreateAccessToken(Node node, Action<Dictionary<string, object>> parameters, TimeSpan duration)
|
public string CreateAccessToken(Node node, Action<Dictionary<string, object>> parameters, TimeSpan duration)
|
||||||
{
|
{
|
||||||
var claims = new Dictionary<string, object>();
|
var claims = new Dictionary<string, object>();
|
||||||
@@ -63,7 +42,7 @@ public class NodeService
|
|||||||
|
|
||||||
public async Task<SystemStatusResponse> GetSystemStatus(Node node)
|
public async Task<SystemStatusResponse> GetSystemStatus(Node node)
|
||||||
{
|
{
|
||||||
using var apiClient = await CreateApiClient(node);
|
using var apiClient = CreateApiClient(node);
|
||||||
return await apiClient.GetJson<SystemStatusResponse>("api/system/status");
|
return await apiClient.GetJson<SystemStatusResponse>("api/system/status");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,21 +50,66 @@ public class NodeService
|
|||||||
|
|
||||||
public async Task<StatisticsApplicationResponse> GetApplicationStatistics(Node node)
|
public async Task<StatisticsApplicationResponse> GetApplicationStatistics(Node node)
|
||||||
{
|
{
|
||||||
using var apiClient = await CreateApiClient(node);
|
using var apiClient = CreateApiClient(node);
|
||||||
return await apiClient.GetJson<StatisticsApplicationResponse>("api/statistics/application");
|
return await apiClient.GetJson<StatisticsApplicationResponse>("api/statistics/application");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StatisticsHostResponse> GetHostStatistics(Node node)
|
public async Task<StatisticsHostResponse> GetHostStatistics(Node node)
|
||||||
{
|
{
|
||||||
using var apiClient = await CreateApiClient(node);
|
using var apiClient = CreateApiClient(node);
|
||||||
return await apiClient.GetJson<StatisticsHostResponse>("api/statistics/host");
|
return await apiClient.GetJson<StatisticsHostResponse>("api/statistics/host");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StatisticsDockerResponse> GetDockerStatistics(Node node)
|
public async Task<StatisticsDockerResponse> GetDockerStatistics(Node node)
|
||||||
{
|
{
|
||||||
using var apiClient = await CreateApiClient(node);
|
using var apiClient = CreateApiClient(node);
|
||||||
return await apiClient.GetJson<StatisticsDockerResponse>("api/statistics/docker");
|
return await apiClient.GetJson<StatisticsDockerResponse>("api/statistics/docker");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
private string GenerateJwt(Node node)
|
||||||
|
{
|
||||||
|
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
|
||||||
|
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||||
|
{
|
||||||
|
//Expires = DateTime.UtcNow.AddYears(1),
|
||||||
|
Expires = DateTime.UtcNow.AddMinutes(1),
|
||||||
|
NotBefore = DateTime.UtcNow.AddSeconds(-1),
|
||||||
|
IssuedAt = DateTime.UtcNow,
|
||||||
|
SigningCredentials = new SigningCredentials(
|
||||||
|
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
||||||
|
node.Token
|
||||||
|
)),
|
||||||
|
SecurityAlgorithms.HmacSha256
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
var securityToken = jwtSecurityTokenHandler.CreateJwtSecurityToken(securityTokenDescriptor);
|
||||||
|
|
||||||
|
return jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpApiClient CreateApiClient(Node node)
|
||||||
|
{
|
||||||
|
var url = "";
|
||||||
|
|
||||||
|
url += node.UseSsl ? "https://" : "http://";
|
||||||
|
url += $"{node.Fqdn}:{node.HttpPort}/";
|
||||||
|
|
||||||
|
var httpClient = new HttpClient()
|
||||||
|
{
|
||||||
|
BaseAddress = new Uri(url)
|
||||||
|
};
|
||||||
|
|
||||||
|
var jwt = GenerateJwt(node);
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
|
||||||
|
|
||||||
|
return new HttpApiClient(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
165
MoonlightServers.ApiServer/Services/ServerService.cs
Normal file
165
MoonlightServers.ApiServer/Services/ServerService.cs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
|
using MoonCore.Extended.Abstractions;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
||||||
|
|
||||||
|
namespace MoonlightServers.ApiServer.Services;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
|
public class ServerService
|
||||||
|
{
|
||||||
|
private readonly NodeService NodeService;
|
||||||
|
private readonly DatabaseRepository<Server> ServerRepository;
|
||||||
|
|
||||||
|
public ServerService(NodeService nodeService, DatabaseRepository<Server> serverRepository)
|
||||||
|
{
|
||||||
|
NodeService = nodeService;
|
||||||
|
ServerRepository = serverRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Power Actions
|
||||||
|
|
||||||
|
public async Task Start(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Post($"api/servers/{server.Id}/start");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Stop(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Post($"api/servers/{server.Id}/stop");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Kill(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Post($"api/servers/{server.Id}/kill");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task Install(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Post($"api/servers/{server.Id}/install");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Sync(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Post($"api/servers/{server.Id}/sync");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SyncDelete(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
await apiClient.Delete($"api/servers/{server.Id}");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerStatusResponse> GetStatus(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
return await apiClient.GetJson<ServerStatusResponse>($"api/servers/{server.Id}/status");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerLogsResponse> GetLogs(Server server)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
return await apiClient.GetJson<ServerLogsResponse>($"api/servers/{server.Id}/logs");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException e)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
public bool IsAllowedToAccess(User user, Server server)
|
||||||
|
{
|
||||||
|
if (server.OwnerId == user.Id)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var permissions = JsonSerializer.Deserialize<string[]>(
|
||||||
|
user.PermissionsJson
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
|
return PermissionHelper.HasPermission(permissions, "admin.servers.get");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<HttpApiClient> GetApiClient(Server server)
|
||||||
|
{
|
||||||
|
var serverWithNode = server;
|
||||||
|
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
// It can be null when its not included when loading via ef !!!
|
||||||
|
if (server.Node == null)
|
||||||
|
{
|
||||||
|
serverWithNode = await ServerRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Node)
|
||||||
|
.FirstAsync(x => x.Id == server.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NodeService.CreateApiClient(serverWithNode.Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -22,9 +22,17 @@ public partial class Server
|
|||||||
|
|
||||||
await LogToConsole("Fetching installation configuration");
|
await LogToConsole("Fetching installation configuration");
|
||||||
|
|
||||||
// Fetching remote configuration
|
// Fetching remote configuration and install config
|
||||||
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
|
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
|
||||||
|
|
||||||
var installData = await remoteService.GetServerInstallation(Configuration.Id);
|
var installData = await remoteService.GetServerInstallation(Configuration.Id);
|
||||||
|
var serverData = await remoteService.GetServer(Configuration.Id);
|
||||||
|
|
||||||
|
// We are updating the regular server config here as well
|
||||||
|
// as changes to variables and other settings wouldn't sync otherwise
|
||||||
|
// because they won't trigger a sync
|
||||||
|
var serverConfiguration = serverData.ToServerConfiguration();
|
||||||
|
UpdateConfiguration(serverConfiguration);
|
||||||
|
|
||||||
var dockerImageService = ServiceProvider.GetRequiredService<DockerImageService>();
|
var dockerImageService = ServiceProvider.GetRequiredService<DockerImageService>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Docker.DotNet;
|
using Docker.DotNet;
|
||||||
using MoonlightServers.Daemon.Enums;
|
using MoonlightServers.Daemon.Enums;
|
||||||
|
using MoonlightServers.Daemon.Extensions;
|
||||||
|
using MoonlightServers.Daemon.Services;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.Abstractions;
|
namespace MoonlightServers.Daemon.Abstractions;
|
||||||
|
|
||||||
@@ -11,6 +13,17 @@ public partial class Server
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await LogToConsole("Fetching configuration");
|
||||||
|
|
||||||
|
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
|
||||||
|
var serverData = await remoteService.GetServer(Configuration.Id);
|
||||||
|
|
||||||
|
// We are updating the server config here
|
||||||
|
// as changes to variables and other settings wouldn't sync otherwise
|
||||||
|
// because they won't trigger a sync
|
||||||
|
var serverConfiguration = serverData.ToServerConfiguration();
|
||||||
|
UpdateConfiguration(serverConfiguration);
|
||||||
|
|
||||||
await ReCreate();
|
await ReCreate();
|
||||||
|
|
||||||
await LogToConsole("Starting container");
|
await LogToConsole("Starting container");
|
||||||
|
|||||||
@@ -49,4 +49,7 @@ public partial class Server
|
|||||||
RuntimeContainerName = $"moonlight-runtime-{Configuration.Id}";
|
RuntimeContainerName = $"moonlight-runtime-{Configuration.Id}";
|
||||||
InstallationContainerName = $"moonlight-install-{Configuration.Id}";
|
InstallationContainerName = $"moonlight-install-{Configuration.Id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateConfiguration(ServerConfiguration configuration)
|
||||||
|
=> Configuration = configuration;
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,35 @@ using Docker.DotNet.Models;
|
|||||||
using Mono.Unix.Native;
|
using Mono.Unix.Native;
|
||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using MoonlightServers.Daemon.Models.Cache;
|
using MoonlightServers.Daemon.Models.Cache;
|
||||||
|
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.Extensions;
|
namespace MoonlightServers.Daemon.Extensions;
|
||||||
|
|
||||||
public static class ServerConfigurationExtensions
|
public static class ServerConfigurationExtensions
|
||||||
{
|
{
|
||||||
|
public static ServerConfiguration ToServerConfiguration(this ServerDataResponse response)
|
||||||
|
{
|
||||||
|
return new ServerConfiguration()
|
||||||
|
{
|
||||||
|
Id = response.Id,
|
||||||
|
StartupCommand = response.StartupCommand,
|
||||||
|
Allocations = response.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration()
|
||||||
|
{
|
||||||
|
IpAddress = y.IpAddress,
|
||||||
|
Port = y.Port
|
||||||
|
}).ToArray(),
|
||||||
|
Variables = response.Variables,
|
||||||
|
OnlineDetection = response.OnlineDetection,
|
||||||
|
DockerImage = response.DockerImage,
|
||||||
|
UseVirtualDisk = response.UseVirtualDisk,
|
||||||
|
Bandwidth = response.Bandwidth,
|
||||||
|
Cpu = response.Cpu,
|
||||||
|
Disk = response.Disk,
|
||||||
|
Memory = response.Memory,
|
||||||
|
StopCommand = response.StopCommand
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static CreateContainerParameters ToRuntimeCreateParameters(this ServerConfiguration configuration,
|
public static CreateContainerParameters ToRuntimeCreateParameters(this ServerConfiguration configuration,
|
||||||
string hostPath, string containerName)
|
string hostPath, string containerName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonlightServers.Daemon.Enums;
|
using MoonlightServers.Daemon.Enums;
|
||||||
@@ -5,6 +6,7 @@ using MoonlightServers.Daemon.Services;
|
|||||||
|
|
||||||
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
|
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/servers")]
|
[Route("api/servers")]
|
||||||
public class ServerPowerController : Controller
|
public class ServerPowerController : Controller
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonlightServers.Daemon.Services;
|
using MoonlightServers.Daemon.Services;
|
||||||
@@ -6,6 +7,7 @@ using MoonlightServers.DaemonShared.Enums;
|
|||||||
|
|
||||||
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
|
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/servers")]
|
[Route("api/servers")]
|
||||||
public class ServersController : Controller
|
public class ServersController : Controller
|
||||||
@@ -17,18 +19,32 @@ public class ServersController : Controller
|
|||||||
ServerService = serverService;
|
ServerService = serverService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{serverId:int}/sync")]
|
||||||
|
public async Task Sync([FromRoute] int serverId)
|
||||||
|
{
|
||||||
|
await ServerService.Sync(serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{serverId:int}")]
|
||||||
|
public async Task Delete([FromRoute] int serverId)
|
||||||
|
{
|
||||||
|
await ServerService.Delete(serverId);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/status")]
|
[HttpGet("{serverId:int}/status")]
|
||||||
public async Task<ServerStatusResponse> GetStatus(int serverId)
|
public Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = ServerService.GetServer(serverId);
|
var server = ServerService.GetServer(serverId);
|
||||||
|
|
||||||
if (server == null)
|
if (server == null)
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
return new ServerStatusResponse()
|
var result = new ServerStatusResponse()
|
||||||
{
|
{
|
||||||
State = (ServerState)server.State
|
State = (ServerState)server.State
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/logs")]
|
[HttpGet("{serverId:int}/logs")]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonlightServers.Daemon.Helpers;
|
using MoonlightServers.Daemon.Helpers;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
||||||
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
|
|||||||
|
|
||||||
// This controller hosts endpoints for the statistics for the daemon application itself
|
// This controller hosts endpoints for the statistics for the daemon application itself
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/statistics/application")]
|
[Route("api/statistics/application")]
|
||||||
public class StatisticsApplicationController : Controller
|
public class StatisticsApplicationController : Controller
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonlightServers.Daemon.Services;
|
using MoonlightServers.Daemon.Services;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
||||||
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
|
|||||||
|
|
||||||
// This controller hosts endpoints for the statistics for the docker environment
|
// This controller hosts endpoints for the statistics for the docker environment
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/statistics/docker")]
|
[Route("api/statistics/docker")]
|
||||||
public class StatisticsDockerController : Controller
|
public class StatisticsDockerController : Controller
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonlightServers.Daemon.Helpers;
|
using MoonlightServers.Daemon.Helpers;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
|
||||||
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
|
|||||||
|
|
||||||
// This controller hosts endpoints for the statistics for host system the daemon runs on
|
// This controller hosts endpoints for the statistics for host system the daemon runs on
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/statistics/host")]
|
[Route("api/statistics/host")]
|
||||||
public class StatisticsHostController : Controller
|
public class StatisticsHostController : Controller
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MoonlightServers.Daemon.Services;
|
using MoonlightServers.Daemon.Services;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys;
|
||||||
|
|
||||||
namespace MoonlightServers.Daemon.Http.Controllers.Sys;
|
namespace MoonlightServers.Daemon.Http.Controllers.Sys;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/system/status")]
|
[Route("api/system/status")]
|
||||||
public class SystemStatusController : Controller
|
public class SystemStatusController : Controller
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ public class RemoteService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ServerDataResponse> GetServer(int serverId)
|
||||||
|
{
|
||||||
|
return await ApiClient.GetJson<ServerDataResponse>(
|
||||||
|
$"api/remote/servers/{serverId}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ServerInstallDataResponse> GetServerInstallation(int serverId)
|
public async Task<ServerInstallDataResponse> GetServerInstallation(int serverId)
|
||||||
{
|
{
|
||||||
return await ApiClient.GetJson<ServerInstallDataResponse>(
|
return await ApiClient.GetJson<ServerInstallDataResponse>(
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using Docker.DotNet;
|
using Docker.DotNet;
|
||||||
using Docker.DotNet.Models;
|
using Docker.DotNet.Models;
|
||||||
using MoonCore.Attributes;
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
using MoonlightServers.Daemon.Abstractions;
|
using MoonlightServers.Daemon.Abstractions;
|
||||||
|
using MoonlightServers.Daemon.Enums;
|
||||||
|
using MoonlightServers.Daemon.Extensions;
|
||||||
using MoonlightServers.Daemon.Models.Cache;
|
using MoonlightServers.Daemon.Models.Cache;
|
||||||
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
|
||||||
|
|
||||||
@@ -16,11 +19,15 @@ public class ServerService : IHostedLifecycleService
|
|||||||
private readonly RemoteService RemoteService;
|
private readonly RemoteService RemoteService;
|
||||||
private readonly IServiceProvider ServiceProvider;
|
private readonly IServiceProvider ServiceProvider;
|
||||||
private readonly ILoggerFactory LoggerFactory;
|
private readonly ILoggerFactory LoggerFactory;
|
||||||
private bool IsInitialized = false;
|
|
||||||
private CancellationTokenSource Cancellation = new();
|
private CancellationTokenSource Cancellation = new();
|
||||||
|
private bool IsInitialized = false;
|
||||||
|
|
||||||
public ServerService(RemoteService remoteService, ILogger<ServerService> logger, IServiceProvider serviceProvider,
|
public ServerService(
|
||||||
ILoggerFactory loggerFactory)
|
RemoteService remoteService,
|
||||||
|
ILogger<ServerService> logger,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
|
)
|
||||||
{
|
{
|
||||||
RemoteService = remoteService;
|
RemoteService = remoteService;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
@@ -35,8 +42,8 @@ public class ServerService : IHostedLifecycleService
|
|||||||
Logger.LogWarning("Ignoring initialize call: Already initialized");
|
Logger.LogWarning("Ignoring initialize call: Already initialized");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
|
|
||||||
// Loading models and converting them
|
// Loading models and converting them
|
||||||
Logger.LogInformation("Fetching servers from panel");
|
Logger.LogInformation("Fetching servers from panel");
|
||||||
@@ -45,25 +52,9 @@ public class ServerService : IHostedLifecycleService
|
|||||||
await RemoteService.GetServers(page, pageSize)
|
await RemoteService.GetServers(page, pageSize)
|
||||||
);
|
);
|
||||||
|
|
||||||
var configurations = servers.Select(x => new ServerConfiguration()
|
var configurations = servers
|
||||||
{
|
.Select(x => x.ToServerConfiguration())
|
||||||
Id = x.Id,
|
.ToArray();
|
||||||
StartupCommand = x.StartupCommand,
|
|
||||||
Allocations = x.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration()
|
|
||||||
{
|
|
||||||
IpAddress = y.IpAddress,
|
|
||||||
Port = y.Port
|
|
||||||
}).ToArray(),
|
|
||||||
Variables = x.Variables,
|
|
||||||
OnlineDetection = x.OnlineDetection,
|
|
||||||
DockerImage = x.DockerImage,
|
|
||||||
UseVirtualDisk = x.UseVirtualDisk,
|
|
||||||
Bandwidth = x.Bandwidth,
|
|
||||||
Cpu = x.Cpu,
|
|
||||||
Disk = x.Disk,
|
|
||||||
Memory = x.Memory,
|
|
||||||
StopCommand = x.StopCommand
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
Logger.LogInformation("Initializing {count} servers", servers.Length);
|
Logger.LogInformation("Initializing {count} servers", servers.Length);
|
||||||
|
|
||||||
@@ -91,7 +82,7 @@ public class ServerService : IHostedLifecycleService
|
|||||||
await Cancellation.CancelAsync();
|
await Cancellation.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AttachToDockerEvents()
|
private Task AttachToDockerEvents()
|
||||||
{
|
{
|
||||||
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
|
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
|
||||||
|
|
||||||
@@ -144,9 +135,11 @@ public class ServerService : IHostedLifecycleService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeServerRange(ServerConfiguration[] serverConfigurations)
|
public async Task InitializeServerRange(ServerConfiguration[] serverConfigurations)
|
||||||
{
|
{
|
||||||
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
|
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
|
||||||
|
|
||||||
@@ -173,7 +166,7 @@ public class ServerService : IHostedLifecycleService
|
|||||||
await InitializeServer(configuration, existingContainers);
|
await InitializeServer(configuration, existingContainers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeServer(
|
public async Task<Server> InitializeServer(
|
||||||
ServerConfiguration serverConfiguration,
|
ServerConfiguration serverConfiguration,
|
||||||
IList<ContainerListResponse> existingContainers
|
IList<ContainerListResponse> existingContainers
|
||||||
)
|
)
|
||||||
@@ -190,6 +183,70 @@ public class ServerService : IHostedLifecycleService
|
|||||||
|
|
||||||
lock (Servers)
|
lock (Servers)
|
||||||
Servers.Add(server);
|
Servers.Add(server);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Sync(int serverId)
|
||||||
|
{
|
||||||
|
var serverData = await RemoteService.GetServer(serverId);
|
||||||
|
var serverConfiguration = serverData.ToServerConfiguration();
|
||||||
|
|
||||||
|
var server = GetServer(serverId);
|
||||||
|
|
||||||
|
if (server == null)
|
||||||
|
await InitializeServer(serverConfiguration, []);
|
||||||
|
else
|
||||||
|
server.UpdateConfiguration(serverConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete(int serverId)
|
||||||
|
{
|
||||||
|
var server = GetServer(serverId);
|
||||||
|
|
||||||
|
// If a server with this id doesn't exist we can just exit
|
||||||
|
if(server == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (server.State == ServerState.Installing)
|
||||||
|
throw new HttpApiException("Unable to delete a server while it is installing", 400);
|
||||||
|
|
||||||
|
#region Callbacks
|
||||||
|
|
||||||
|
var deleteCompletion = new TaskCompletionSource();
|
||||||
|
|
||||||
|
async Task HandleStateChange(ServerState state)
|
||||||
|
{
|
||||||
|
if (state == ServerState.Offline)
|
||||||
|
await DeleteServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task DeleteServer()
|
||||||
|
{
|
||||||
|
await server.CancelTasks();
|
||||||
|
await server.RemoveInstallationVolume();
|
||||||
|
await server.RemoveRuntimeVolume();
|
||||||
|
|
||||||
|
deleteCompletion.SetResult();
|
||||||
|
|
||||||
|
lock (Servers)
|
||||||
|
Servers.Remove(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// If the server is still online, we are killing it and then
|
||||||
|
// waiting for the callback to trigger notifying us that the server is now offline
|
||||||
|
// so we can delete it. The request will pause until then using the deleteCompletion task
|
||||||
|
if (server.State != ServerState.Offline)
|
||||||
|
{
|
||||||
|
server.OnStateChanged += HandleStateChange;
|
||||||
|
await server.Kill();
|
||||||
|
|
||||||
|
await deleteCompletion.Task;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await DeleteServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Server? GetServer(int id)
|
public Server? GetServer(int id)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Docker.DotNet;
|
using Docker.DotNet;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using MoonCore.Configuration;
|
using MoonCore.Configuration;
|
||||||
using MoonCore.EnvConfiguration;
|
using MoonCore.EnvConfiguration;
|
||||||
using MoonCore.Extended.Extensions;
|
using MoonCore.Extended.Extensions;
|
||||||
@@ -41,6 +44,7 @@ public class Startup
|
|||||||
await RegisterAppConfiguration();
|
await RegisterAppConfiguration();
|
||||||
await RegisterLogging();
|
await RegisterLogging();
|
||||||
await RegisterBase();
|
await RegisterBase();
|
||||||
|
await RegisterAuth();
|
||||||
await RegisterDocker();
|
await RegisterDocker();
|
||||||
await RegisterServers();
|
await RegisterServers();
|
||||||
await RegisterSignalR();
|
await RegisterSignalR();
|
||||||
@@ -49,6 +53,7 @@ public class Startup
|
|||||||
await BuildWebApplication();
|
await BuildWebApplication();
|
||||||
|
|
||||||
await UseBase();
|
await UseBase();
|
||||||
|
await UseAuth();
|
||||||
await UseCors();
|
await UseCors();
|
||||||
await UseBaseMiddleware();
|
await UseBaseMiddleware();
|
||||||
|
|
||||||
@@ -289,4 +294,40 @@ public class Startup
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Authentication
|
||||||
|
|
||||||
|
private Task RegisterAuth()
|
||||||
|
{
|
||||||
|
WebApplicationBuilder.Services
|
||||||
|
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new()
|
||||||
|
{
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
||||||
|
Configuration.Security.Token
|
||||||
|
)),
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
WebApplicationBuilder.Services.AddAuthorization();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task UseAuth()
|
||||||
|
{
|
||||||
|
WebApplication.UseAuthentication();
|
||||||
|
WebApplication.UseAuthorization();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
84
MoonlightServers.Frontend/Services/ServerService.cs
Normal file
84
MoonlightServers.Frontend/Services/ServerService.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Models;
|
||||||
|
using MoonlightServers.Shared.Http.Responses.Users.Servers;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Frontend.Services;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
|
public class ServerService
|
||||||
|
{
|
||||||
|
private readonly HttpApiClient HttpApiClient;
|
||||||
|
|
||||||
|
public ServerService(HttpApiClient httpApiClient)
|
||||||
|
{
|
||||||
|
HttpApiClient = httpApiClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedData<ServerDetailResponse>> GetServers(int page, int perPage)
|
||||||
|
{
|
||||||
|
return await HttpApiClient.GetJson<PagedData<ServerDetailResponse>>(
|
||||||
|
$"api/client/servers?page={page}&pageSize={perPage}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerDetailResponse> GetServer(int serverId)
|
||||||
|
{
|
||||||
|
return await HttpApiClient.GetJson<ServerDetailResponse>(
|
||||||
|
$"api/client/servers/{serverId}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerStatusResponse> GetStatus(int serverId)
|
||||||
|
{
|
||||||
|
return await HttpApiClient.GetJson<ServerStatusResponse>(
|
||||||
|
$"api/client/servers/{serverId}/status"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerLogsResponse> GetLogs(int serverId)
|
||||||
|
{
|
||||||
|
return await HttpApiClient.GetJson<ServerLogsResponse>(
|
||||||
|
$"api/client/servers/{serverId}/logs"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerWebSocketResponse> GetWebSocket(int serverId)
|
||||||
|
{
|
||||||
|
return await HttpApiClient.GetJson<ServerWebSocketResponse>(
|
||||||
|
$"api/client/servers/{serverId}/ws"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Install(int serverId)
|
||||||
|
{
|
||||||
|
await HttpApiClient.Post(
|
||||||
|
$"api/client/servers/{serverId}/install"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Power actions
|
||||||
|
|
||||||
|
public async Task Start(int serverId)
|
||||||
|
{
|
||||||
|
await HttpApiClient.Post(
|
||||||
|
$"api/client/servers/{serverId}/start"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Stop(int serverId)
|
||||||
|
{
|
||||||
|
await HttpApiClient.Post(
|
||||||
|
$"api/client/servers/{serverId}/stop"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Kill(int serverId)
|
||||||
|
{
|
||||||
|
await HttpApiClient.Post(
|
||||||
|
$"api/client/servers/{serverId}/kill"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonlightServers.Frontend.Services
|
||||||
@using MoonlightServers.Shared.Enums
|
@using MoonlightServers.Shared.Enums
|
||||||
@using MoonlightServers.Shared.Http.Responses.Users.Servers
|
@using MoonlightServers.Shared.Http.Responses.Users.Servers
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject ServerService ServerService
|
||||||
@inject ILogger<ServerCard> Logger
|
@inject ILogger<ServerCard> Logger
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@@ -158,9 +158,7 @@
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Status = await ApiClient.GetJson<ServerStatusResponse>(
|
Status = await ServerService.GetStatus(Server.Id);
|
||||||
$"api/client/servers/{Server.Id}/status"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
@using MoonCore.Blazor.Tailwind.Alerts
|
@using MoonCore.Blazor.Tailwind.Alerts
|
||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.Blazor.Tailwind.Components
|
@using MoonCore.Blazor.Tailwind.Components
|
||||||
|
@using MoonlightServers.Frontend.Services
|
||||||
@using MoonlightServers.Shared.Enums
|
@using MoonlightServers.Shared.Enums
|
||||||
|
|
||||||
@inherits BaseServerTab
|
@inherits BaseServerTab
|
||||||
|
|
||||||
@inject HttpApiClient HttpApiClient
|
@inject ServerService ServerService
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:col-span-2 lg:grid-cols-3">
|
<div class="grid grid-cols-1 md:col-span-2 lg:grid-cols-3">
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
await AlertService.ConfirmDanger(
|
await AlertService.ConfirmDanger(
|
||||||
"Server installation",
|
"Server installation",
|
||||||
"Do you really want to reinstall the server? This can potentially lead to loss of data",
|
"Do you really want to reinstall the server? This can potentially lead to loss of data",
|
||||||
() => HttpApiClient.Post($"api/client/servers/{Server.Id}/install")
|
() => ServerService.Install(Server.Id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
@page "/servers"
|
@page "/servers"
|
||||||
|
|
||||||
@using MoonCore.Helpers
|
|
||||||
@using MoonlightServers.Frontend.UI.Components.Servers
|
@using MoonlightServers.Frontend.UI.Components.Servers
|
||||||
@using MoonCore.Blazor.Tailwind.Components
|
@using MoonCore.Blazor.Tailwind.Components
|
||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
|
@using MoonlightServers.Frontend.Services
|
||||||
@using MoonlightServers.Shared.Http.Responses.Users.Servers
|
@using MoonlightServers.Shared.Http.Responses.Users.Servers
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject ServerService ServerService
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<div class="flex flex-col gap-y-5">
|
<div class="flex flex-col gap-y-5">
|
||||||
@@ -42,9 +42,7 @@
|
|||||||
private async Task Load(LazyLoader lazyLoader)
|
private async Task Load(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
Servers = await PagedData<ServerDetailResponse>.All(async (page, pageSize) =>
|
Servers = await PagedData<ServerDetailResponse>.All(async (page, pageSize) =>
|
||||||
await ApiClient.GetJson<PagedData<ServerDetailResponse>>(
|
await ServerService.GetServers(page, pageSize)
|
||||||
$"api/client/servers?page={page}&pageSize={pageSize}"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,12 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonlightServers.Frontend.Interfaces
|
@using MoonlightServers.Frontend.Interfaces
|
||||||
@using MoonlightServers.Frontend.Models
|
@using MoonlightServers.Frontend.Models
|
||||||
|
@using MoonlightServers.Frontend.Services
|
||||||
@using MoonlightServers.Shared.Enums
|
@using MoonlightServers.Shared.Enums
|
||||||
@using MoonlightServers.Frontend.UI.Components
|
@using MoonlightServers.Frontend.UI.Components
|
||||||
@using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs
|
@using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject ServerService ServerService
|
||||||
@inject IEnumerable<IServerTabProvider> TabProviders
|
@inject IEnumerable<IServerTabProvider> TabProviders
|
||||||
|
|
||||||
@implements IAsyncDisposable
|
@implements IAsyncDisposable
|
||||||
@@ -164,25 +165,19 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Load meta data
|
// Load meta data
|
||||||
Server = await ApiClient.GetJson<ServerDetailResponse>(
|
Server = await ServerService.GetServer(ServerId);
|
||||||
$"api/client/servers/{ServerId}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load server tabs
|
// Load server tabs
|
||||||
foreach (var serverTabProvider in TabProviders)
|
foreach (var serverTabProvider in TabProviders)
|
||||||
Tabs.AddRange(await serverTabProvider.GetTabs(Server));
|
Tabs.AddRange(await serverTabProvider.GetTabs(Server));
|
||||||
|
|
||||||
// Load initial status for first render
|
// Load initial status for first render
|
||||||
var status = await ApiClient.GetJson<ServerStatusResponse>(
|
var status = await ServerService.GetStatus(ServerId);
|
||||||
$"api/client/servers/{ServerId}/status"
|
|
||||||
);
|
|
||||||
|
|
||||||
State = status.State;
|
State = status.State;
|
||||||
|
|
||||||
// Load initial messages
|
// Load initial messages
|
||||||
var initialLogs = await ApiClient.GetJson<ServerLogsResponse>(
|
var initialLogs = await ServerService.GetLogs(ServerId);
|
||||||
$"api/client/servers/{ServerId}/logs"
|
|
||||||
);
|
|
||||||
|
|
||||||
InitialConsoleMessage = "";
|
InitialConsoleMessage = "";
|
||||||
|
|
||||||
@@ -190,9 +185,7 @@
|
|||||||
InitialConsoleMessage += message;
|
InitialConsoleMessage += message;
|
||||||
|
|
||||||
// Load websocket meta
|
// Load websocket meta
|
||||||
var websocketDetails = await ApiClient.GetJson<ServerWebSocketResponse>(
|
var websocketDetails = await ServerService.GetWebSocket(ServerId);
|
||||||
$"api/client/servers/{ServerId}/ws"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build signal r
|
// Build signal r
|
||||||
HubConnection = new HubConnectionBuilder()
|
HubConnection = new HubConnectionBuilder()
|
||||||
@@ -232,13 +225,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task Start()
|
private async Task Start()
|
||||||
=> await ApiClient.Post($"api/client/servers/{Server.Id}/start");
|
=> await ServerService.Start(ServerId);
|
||||||
|
|
||||||
private async Task Stop()
|
private async Task Stop()
|
||||||
=> await ApiClient.Post($"api/client/servers/{Server.Id}/stop");
|
=> await ServerService.Stop(ServerId);
|
||||||
|
|
||||||
private async Task Kill()
|
private async Task Kill()
|
||||||
=> await ApiClient.Post($"api/client/servers/{Server.Id}/kill");
|
=> await ServerService.Kill(ServerId);
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class CreateServerRequest
|
|||||||
|
|
||||||
public string? StartupOverride { get; set; }
|
public string? StartupOverride { get; set; }
|
||||||
|
|
||||||
public int DockerImageIndex { get; set; }
|
public int DockerImageIndex { get; set; } = -1;
|
||||||
|
|
||||||
public int StarId { get; set; }
|
public int StarId { get; set; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user