Added server tab provider. Moved power actions to seperate controller

This commit is contained in:
2025-02-22 20:08:33 +01:00
parent c452e652a2
commit 67efe71247
11 changed files with 258 additions and 70 deletions

View File

@@ -0,0 +1,125 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MoonCore.Exceptions;
using MoonCore.Extended.Abstractions;
using MoonCore.Extensions;
using MoonlightServers.ApiServer.Database.Entities;
using MoonlightServers.ApiServer.Services;
namespace MoonlightServers.ApiServer.Http.Controllers.Users;
[ApiController]
[Authorize]
[Route("api/servers")]
public class ServerPowerController : Controller
{
private readonly DatabaseRepository<Server> ServerRepository;
private readonly NodeService NodeService;
public ServerPowerController(DatabaseRepository<Server> serverRepository, NodeService nodeService)
{
ServerRepository = serverRepository;
NodeService = nodeService;
}
[HttpPost("{serverId:int}/start")]
[Authorize]
public async Task Start([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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")]
[Authorize]
public async Task Stop([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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")]
[Authorize]
public async Task Kill([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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")]
[Authorize]
public async Task Install([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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,
Func<IQueryable<Server>, IQueryable<Server>>? queryModifier = null)
{
var userIdClaim = User.Claims.First(x => x.Type == "userId");
var userId = int.Parse(userIdClaim.Value);
var query = ServerRepository
.Get()
.Include(x => x.Node) as IQueryable<Server>;
if (queryModifier != null)
query = queryModifier.Invoke(query);
var server = await query
.FirstOrDefaultAsync(x => x.Id == serverId);
if (server == null)
throw new HttpApiException("No server with this id found", 404);
if (server.OwnerId == userId) // The current user is the owner
return server;
if (User.HasPermission("admin.servers.get")) // The current user is an admin
return server;
throw new HttpApiException("No server with this id found", 404);
}
}

View File

@@ -175,60 +175,6 @@ public class ServersController : Controller
throw new HttpApiException("Unable to access the node the server is running on", 502); throw new HttpApiException("Unable to access the node the server is running on", 502);
} }
} }
[HttpPost("{serverId:int}/start")]
[Authorize]
public async Task Start([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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")]
[Authorize]
public async Task Stop([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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")]
[Authorize]
public async Task Kill([FromRoute] int serverId)
{
var server = await GetServerWithPermCheck(serverId);
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);
}
}
private async Task<Server> GetServerWithPermCheck(int serverId, private async Task<Server> GetServerWithPermCheck(int serverId,
Func<IQueryable<Server>, IQueryable<Server>>? queryModifier = null) Func<IQueryable<Server>, IQueryable<Server>>? queryModifier = null)

View File

@@ -0,0 +1,21 @@
using MoonlightServers.Frontend.Interfaces;
using MoonlightServers.Frontend.Models;
using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs;
using MoonlightServers.Shared.Http.Responses.Users.Servers;
namespace MoonlightServers.Frontend.Implementations;
public class DefaultServerTabProvider : IServerTabProvider
{
public Task<ServerTab[]> GetTabs(ServerDetailResponse server)
{
ServerTab[] tabs =
[
ServerTab.CreateFromComponent<ConsoleTab>("Console", "console", 0),
ServerTab.CreateFromComponent<FilesTab>("Files", "files", 1),
ServerTab.CreateFromComponent<SettingsTab>("Settings", "settings", 10),
];
return Task.FromResult(tabs);
}
}

View File

@@ -0,0 +1,9 @@
using MoonlightServers.Frontend.Models;
using MoonlightServers.Shared.Http.Responses.Users.Servers;
namespace MoonlightServers.Frontend.Interfaces;
public interface IServerTabProvider
{
public Task<ServerTab[]> GetTabs(ServerDetailResponse server);
}

View File

@@ -1,7 +1,22 @@
using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs;
namespace MoonlightServers.Frontend.Models; namespace MoonlightServers.Frontend.Models;
public class ServerTab public class ServerTab
{ {
public string Path { get; set; } public string Name { get; private set; }
public string Name { get; set; } public string Path { get; private set; }
public int Priority { get; set; }
public Type ComponentType { get; private set; }
public static ServerTab CreateFromComponent<T>(string name, string path, int priority) where T : BaseServerTab
{
return new()
{
Name = name,
Path = path,
Priority = priority,
ComponentType = typeof(T)
};
}
} }

View File

@@ -21,7 +21,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="Helpers\"/> <Folder Include="Helpers\"/>
<Folder Include="Interfaces\"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MoonCore.Extensions; using MoonCore.Extensions;
using MoonCore.PluginFramework.Extensions;
using Moonlight.Client.Interfaces; using Moonlight.Client.Interfaces;
using MoonlightServers.Frontend.Interfaces;
namespace MoonlightServers.Frontend.Startup; namespace MoonlightServers.Frontend.Startup;
@@ -9,6 +11,13 @@ public class PluginStartup : IAppStartup
public Task BuildApp(WebAssemblyHostBuilder builder) public Task BuildApp(WebAssemblyHostBuilder builder)
{ {
builder.Services.AutoAddServices<PluginStartup>(); builder.Services.AutoAddServices<PluginStartup>();
builder.Services.AddInterfaces(configuration =>
{
configuration.AddAssembly(GetType().Assembly);
configuration.AddInterface<IServerTabProvider>();
});
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -4,7 +4,6 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "Styles",
"dependencies": { "dependencies": {
"@tailwindcss/forms": "^0.5.9" "@tailwindcss/forms": "^0.5.9"
}, },

View File

@@ -0,0 +1,6 @@
@inherits BaseServerTab
@code
{
}

View File

@@ -0,0 +1,40 @@
@using MoonCore.Blazor.Tailwind.Alerts
@using MoonCore.Helpers
@using MoonCore.Blazor.Tailwind.Components
@using MoonlightServers.Shared.Enums
@inherits BaseServerTab
@inject HttpApiClient HttpApiClient
@inject AlertService AlertService
<div class="grid grid-cols-1 md:col-span-2 lg:grid-cols-3">
<div class="col-span-1 card card-body">
@if (State != ServerState.Offline)
{
<button class="btn btn-primary" disabled="disabled">
<i class="align-middle icon-hammer me-1"></i>
<span class="align-middle">Reinstall</span>
</button>
}
else
{
<WButton CssClasses="btn btn-primary" OnClick="Reinstall">
<i class="align-middle icon-hammer me-1"></i>
<span class="align-middle">Reinstall</span>
</WButton>
}
</div>
</div>
@code
{
private async Task Reinstall(WButton _)
{
await AlertService.ConfirmDanger(
"Server installation",
"Do you really want to reinstall the server? This can potentially lead to loss of data",
() => HttpApiClient.Post($"api/servers/{Server.Id}/install")
);
}
}

View File

@@ -5,11 +5,14 @@
@using MoonCore.Blazor.Tailwind.Components @using MoonCore.Blazor.Tailwind.Components
@using MoonCore.Exceptions @using MoonCore.Exceptions
@using MoonCore.Helpers @using MoonCore.Helpers
@using MoonlightServers.Frontend.Interfaces
@using MoonlightServers.Frontend.Models
@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 HttpApiClient ApiClient
@inject IServerTabProvider[] TabProviders
@implements IAsyncDisposable @implements IAsyncDisposable
@@ -75,7 +78,7 @@
<span class="align-middle">Start</span> <span class="align-middle">Start</span>
</button> </button>
} }
@if (State == ServerState.Online) @if (State == ServerState.Online)
{ {
<button type="button" class="btn btn-primary"> <button type="button" class="btn btn-primary">
@@ -90,7 +93,7 @@
<span class="align-middle">Restart</span> <span class="align-middle">Restart</span>
</button> </button>
} }
@if (State == ServerState.Starting || State == ServerState.Online || State == ServerState.Stopping) @if (State == ServerState.Starting || State == ServerState.Online || State == ServerState.Stopping)
{ {
if (State == ServerState.Stopping) if (State == ServerState.Stopping)
@@ -118,16 +121,26 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mt-5">
<Tabs NavStyle="true">
<Tab Name="Console">
<ConsoleTab Parent="this" Server="Server" State="State" HubConnection="HubConnection" InitialConsoleMessage="@InitialConsoleMessage" />
</Tab>
<Tab Name="Testy"> <div class="mt-3">
<Tabs>
</Tab> @foreach (var tab in Tabs)
{
<Tab Name="@tab.Name">
@{
var rf = ComponentHelper.FromType(tab.ComponentType, parameters =>
{
parameters.Add("Server", Server);
parameters.Add("State", State);
parameters.Add("InitialConsoleMessage", InitialConsoleMessage);
parameters.Add("HubConnection", HubConnection);
parameters.Add("Parent", this);
});
}
@rf
</Tab>
}
</Tabs> </Tabs>
</div> </div>
} }
@@ -137,6 +150,8 @@
{ {
[Parameter] public int ServerId { get; set; } [Parameter] public int ServerId { get; set; }
private List<ServerTab> Tabs = new();
private ServerDetailResponse Server; private ServerDetailResponse Server;
private bool NotFound = false; private bool NotFound = false;
private ServerState State; private ServerState State;
@@ -153,6 +168,10 @@
$"api/servers/{ServerId}" $"api/servers/{ServerId}"
); );
// Load server tabs
foreach (var serverTabProvider in TabProviders)
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 ApiClient.GetJson<ServerStatusResponse>(
$"api/servers/{ServerId}/status" $"api/servers/{ServerId}/status"
@@ -217,7 +236,7 @@
private async Task Stop() private async Task Stop()
=> await ApiClient.Post($"api/servers/{Server.Id}/stop"); => await ApiClient.Post($"api/servers/{Server.Id}/stop");
private async Task Kill() private async Task Kill()
=> await ApiClient.Post($"api/servers/{Server.Id}/kill"); => await ApiClient.Post($"api/servers/{Server.Id}/kill");