Refactored ui. Improved console experience. Added command endpoint
This commit is contained in:
@@ -68,6 +68,7 @@ public class ServersController : Controller
|
|||||||
.Include(x => x.Star)
|
.Include(x => x.Star)
|
||||||
.Skip(page * pageSize)
|
.Skip(page * pageSize)
|
||||||
.Take(pageSize)
|
.Take(pageSize)
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
var mappedItems = items
|
var mappedItems = items
|
||||||
@@ -213,7 +214,7 @@ public class ServersController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
[HttpPatch("{id:int}")]
|
||||||
[Authorize(Policy = "permissions.admin.servers.write")]
|
[Authorize(Policy = "permissions:admin.servers.write")]
|
||||||
public async Task<ServerResponse> Update([FromRoute] int id, [FromBody] UpdateServerRequest request)
|
public async Task<ServerResponse> Update([FromRoute] int id, [FromBody] UpdateServerRequest request)
|
||||||
{
|
{
|
||||||
//TODO: Handle shrinking virtual disk
|
//TODO: Handle shrinking virtual disk
|
||||||
@@ -294,11 +295,16 @@ public class ServersController : Controller
|
|||||||
.Include(x => x.Star)
|
.Include(x => x.Star)
|
||||||
.Include(x => x.Variables)
|
.Include(x => x.Variables)
|
||||||
.Include(x => x.Backups)
|
.Include(x => x.Backups)
|
||||||
|
.Include(x => x.Allocations)
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (server == null)
|
if (server == null)
|
||||||
throw new HttpApiException("No server with that id found", 404);
|
throw new HttpApiException("No server with that id found", 404);
|
||||||
|
|
||||||
|
server.Variables.Clear();
|
||||||
|
server.Backups.Clear();
|
||||||
|
server.Allocations.Clear();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// If the sync fails on the node and we aren't forcing the deletion,
|
// If the sync fails on the node and we aren't forcing the deletion,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using MoonlightServers.ApiServer.Extensions;
|
|||||||
using MoonlightServers.ApiServer.Models;
|
using MoonlightServers.ApiServer.Models;
|
||||||
using MoonlightServers.ApiServer.Services;
|
using MoonlightServers.ApiServer.Services;
|
||||||
using MoonlightServers.Shared.Enums;
|
using MoonlightServers.Shared.Enums;
|
||||||
|
using MoonlightServers.Shared.Http.Requests.Client.Servers;
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers;
|
using MoonlightServers.Shared.Http.Responses.Client.Servers;
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations;
|
using MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations;
|
||||||
using MoonlightServers.Shared.Models;
|
using MoonlightServers.Shared.Models;
|
||||||
@@ -293,6 +294,17 @@ public class ServersController : Controller
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{serverId:int}/command")]
|
||||||
|
public async Task RunCommand([FromRoute] int serverId, [FromBody] ServerCommandRequest request)
|
||||||
|
{
|
||||||
|
var server = await GetServerById(
|
||||||
|
serverId,
|
||||||
|
permission => permission is { Name: "console", Type: >= ServerPermissionType.ReadWrite }
|
||||||
|
);
|
||||||
|
|
||||||
|
await ServerService.RunCommand(server, request.Command);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId, Func<ServerSharePermission, bool>? filter = null)
|
private async Task<Server> GetServerById(int serverId, Func<ServerSharePermission, bool>? filter = null)
|
||||||
{
|
{
|
||||||
var server = await ServerRepository
|
var server = await ServerRepository
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using MoonCore.Extended.Abstractions;
|
|||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
using MoonlightServers.ApiServer.Database.Entities;
|
||||||
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Requests;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Services;
|
namespace MoonlightServers.ApiServer.Services;
|
||||||
@@ -137,7 +138,27 @@ public class ServerService
|
|||||||
using var apiClient = await GetApiClient(server);
|
using var apiClient = await GetApiClient(server);
|
||||||
return await apiClient.GetJson<ServerStatsResponse>($"api/servers/{server.Id}/stats");
|
return await apiClient.GetJson<ServerStatsResponse>($"api/servers/{server.Id}/stats");
|
||||||
}
|
}
|
||||||
catch (HttpRequestException e)
|
catch (HttpRequestException)
|
||||||
|
{
|
||||||
|
throw new HttpApiException("Unable to access the node the server is running on", 502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RunCommand(Server server, string command)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var apiClient = await GetApiClient(server);
|
||||||
|
|
||||||
|
await apiClient.Post(
|
||||||
|
$"api/servers/{server.Id}/command",
|
||||||
|
new ServerCommandRequest()
|
||||||
|
{
|
||||||
|
Command = command
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException)
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonlightServers.Daemon.ServerSystem.SubSystems;
|
using MoonlightServers.Daemon.ServerSystem.SubSystems;
|
||||||
using MoonlightServers.Daemon.Services;
|
using MoonlightServers.Daemon.Services;
|
||||||
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Requests;
|
||||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
||||||
using MoonlightServers.DaemonShared.Enums;
|
using MoonlightServers.DaemonShared.Enums;
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Servers;
|
|||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/servers")]
|
[Route("api/servers/{serverId:int}")]
|
||||||
public class ServersController : Controller
|
public class ServersController : Controller
|
||||||
{
|
{
|
||||||
private readonly ServerService ServerService;
|
private readonly ServerService ServerService;
|
||||||
@@ -20,19 +21,19 @@ public class ServersController : Controller
|
|||||||
ServerService = serverService;
|
ServerService = serverService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/sync")]
|
[HttpPost("sync")]
|
||||||
public async Task Sync([FromRoute] int serverId)
|
public async Task Sync([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
await ServerService.Sync(serverId);
|
await ServerService.Sync(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{serverId:int}")]
|
[HttpDelete]
|
||||||
public async Task Delete([FromRoute] int serverId)
|
public async Task Delete([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
await ServerService.Delete(serverId);
|
await ServerService.Delete(serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/status")]
|
[HttpGet("status")]
|
||||||
public Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
public Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = ServerService.Find(serverId);
|
var server = ServerService.Find(serverId);
|
||||||
@@ -48,7 +49,7 @@ public class ServersController : Controller
|
|||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/logs")]
|
[HttpGet("logs")]
|
||||||
public async Task<ServerLogsResponse> GetLogs([FromRoute] int serverId)
|
public async Task<ServerLogsResponse> GetLogs([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = ServerService.Find(serverId);
|
var server = ServerService.Find(serverId);
|
||||||
@@ -65,7 +66,7 @@ public class ServersController : Controller
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/stats")]
|
[HttpGet("stats")]
|
||||||
public Task<ServerStatsResponse> GetStats([FromRoute] int serverId)
|
public Task<ServerStatsResponse> GetStats([FromRoute] int serverId)
|
||||||
{
|
{
|
||||||
var server = ServerService.Find(serverId);
|
var server = ServerService.Find(serverId);
|
||||||
@@ -85,4 +86,17 @@ public class ServersController : Controller
|
|||||||
IoWrite = statsSubSystem.CurrentStats.IoWrite
|
IoWrite = statsSubSystem.CurrentStats.IoWrite
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("command")]
|
||||||
|
public async Task Command([FromRoute] int serverId, [FromBody] ServerCommandRequest request)
|
||||||
|
{
|
||||||
|
var server = ServerService.Find(serverId);
|
||||||
|
|
||||||
|
if (server == null)
|
||||||
|
throw new HttpApiException("No server with this id found", 404);
|
||||||
|
|
||||||
|
var consoleSubSystem = server.GetRequiredSubSystem<ConsoleSubSystem>();
|
||||||
|
|
||||||
|
await consoleSubSystem.WriteInput(request.Command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,13 @@
|
|||||||
<_ContentIncludedByDefault Remove="storage\volumes\2\usercache.json" />
|
<_ContentIncludedByDefault Remove="storage\volumes\2\usercache.json" />
|
||||||
<_ContentIncludedByDefault Remove="storage\volumes\2\version_history.json" />
|
<_ContentIncludedByDefault Remove="storage\volumes\2\version_history.json" />
|
||||||
<_ContentIncludedByDefault Remove="storage\volumes\2\whitelist.json" />
|
<_ContentIncludedByDefault Remove="storage\volumes\2\whitelist.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\banned-ips.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\banned-players.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\ops.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\plugins\spark\config.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\usercache.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\version_history.json" />
|
||||||
|
<_ContentIncludedByDefault Remove="volumes\3\whitelist.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MoonlightServers.DaemonShared.DaemonSide.Http.Requests;
|
||||||
|
|
||||||
|
public class ServerCommandRequest
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "The command is required")]
|
||||||
|
public string Command { get; set; }
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using MoonCore.Attributes;
|
using MoonCore.Attributes;
|
||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
|
using MoonlightServers.Shared.Http.Requests.Client.Servers;
|
||||||
using MoonlightServers.Shared.Http.Requests.Client.Servers.Variables;
|
using MoonlightServers.Shared.Http.Requests.Client.Servers.Variables;
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers;
|
using MoonlightServers.Shared.Http.Responses.Client.Servers;
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables;
|
using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables;
|
||||||
@@ -59,6 +60,17 @@ public class ServerService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RunCommand(int serverId, string command)
|
||||||
|
{
|
||||||
|
await HttpApiClient.Post(
|
||||||
|
$"api/client/servers/{serverId}/command",
|
||||||
|
new ServerCommandRequest()
|
||||||
|
{
|
||||||
|
Command = command
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ServerWebSocketResponse> GetWebSocket(int serverId)
|
public async Task<ServerWebSocketResponse> GetWebSocket(int serverId)
|
||||||
{
|
{
|
||||||
return await HttpApiClient.GetJson<ServerWebSocketResponse>(
|
return await HttpApiClient.GetJson<ServerWebSocketResponse>(
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
@using System.Text.Json.Serialization
|
||||||
|
@using Microsoft.Extensions.Logging
|
||||||
|
@using XtermBlazor
|
||||||
|
@inherits MoonCore.Blazor.FlyonUi.Modals.Components.BaseModal
|
||||||
|
|
||||||
|
@inject IJSRuntime JsRuntime
|
||||||
|
@inject ILogger<FullScreenModal> Logger
|
||||||
|
|
||||||
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
|
<div class="bg-black p-2 relative w-full h-[90vh] rounded-lg">
|
||||||
|
@if (IsInitialized)
|
||||||
|
{
|
||||||
|
<Xterm @ref="Terminal"
|
||||||
|
Addons="Parent.Addons"
|
||||||
|
Options="Parent.Options"
|
||||||
|
Class="h-full w-full"
|
||||||
|
OnFirstRender="HandleFirstRender"/>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="absolute top-4 right-4">
|
||||||
|
<button @onclick="Hide" class="btn btn-error btn-square">
|
||||||
|
<i class="icon-x text-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter] public XtermConsole Parent { get; set; }
|
||||||
|
|
||||||
|
private bool IsInitialized = false;
|
||||||
|
private bool IsReadyToWrite = false;
|
||||||
|
private Xterm Terminal;
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (!firstRender)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Initialize addons
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlightServers.loadAddons");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError("An error occured while initializing addons: {e}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to parent events
|
||||||
|
Parent.OnWrite += HandleWrite;
|
||||||
|
|
||||||
|
IsInitialized = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleFirstRender()
|
||||||
|
{
|
||||||
|
IsReadyToWrite = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Terminal.Addon("addon-fit").InvokeVoidAsync("fit");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError("An error occured while calling addons: {e}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputToWrite = string.Concat(Parent.OutputCache.ToArray());
|
||||||
|
await Terminal.Write(outputToWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleWrite(string content)
|
||||||
|
{
|
||||||
|
if (!IsReadyToWrite)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Terminal.Write(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
Parent.OnWrite -= HandleWrite;
|
||||||
|
await Terminal.DisposeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<div class="col-span-1">
|
<div class="col-span-1">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Actions
|
<span class="card-title">Actions</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="flex flex-col gap-y-3">
|
<div class="flex flex-col gap-y-3">
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
@inject NodeService NodeService
|
@inject NodeService NodeService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject ILogger<OverviewNodeUpdate> Logger
|
@inject ILogger<Overview> Logger
|
||||||
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<i class="icon-cpu text-4xl text-primary"></i>
|
<i class="icon-cpu text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
CPU: @Statistics.Cpu.Model
|
CPU: @Statistics.Cpu.Model
|
||||||
</span>
|
</span>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<i class="icon-memory-stick text-4xl text-primary"></i>
|
<i class="icon-memory-stick text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
Memory
|
Memory
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<i class="icon-shapes text-4xl text-primary"></i>
|
<i class="icon-shapes text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
Swap
|
Swap
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
var percentRounded = Math.Round(usage, 2);
|
var percentRounded = Math.Round(usage, 2);
|
||||||
|
|
||||||
<div class="flex flex-row items-center col-span-1">
|
<div class="flex flex-row items-center col-span-1">
|
||||||
<div class="text-sm text-slate-300 me-1.5 grow-0 flex flex-col">
|
<div class="text-sm text-base-content/90 me-1.5 grow-0 flex flex-col">
|
||||||
<span>#@(i)</span>
|
<span>#@(i)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-slate-300 mt-2.5 flex flex-col">
|
<div class="text-sm text-base-content/90 mt-2.5 flex flex-col">
|
||||||
<div>
|
<div>
|
||||||
Device: <span class="font-semibold">@disk.Device</span> - Mounted at: <span class="font-semibold truncate">@disk.MountPath</span>
|
Device: <span class="font-semibold">@disk.Device</span> - Mounted at: <span class="font-semibold truncate">@disk.MountPath</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<i class="icon-gallery-horizontal-end text-4xl text-primary"></i>
|
<i class="icon-gallery-horizontal-end text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
Images
|
Images
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<i class="icon-container text-4xl text-primary"></i>
|
<i class="icon-container text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
Containers
|
Containers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<i class="icon-hard-hat text-4xl text-primary"></i>
|
<i class="icon-hard-hat text-4xl text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-base text-slate-300">
|
<div class="text-base text-base-content/90">
|
||||||
Build Cache
|
Build Cache
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
@inject ILogger<ServerCard> Logger
|
@inject ILogger<ServerCard> Logger
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var gradient = "from-base-content/20";
|
var gradient = "from-base-100/20";
|
||||||
var border = "border-base-content";
|
var border = "border-base-content/80";
|
||||||
|
|
||||||
if (IsLoaded && !IsFailed)
|
if (IsLoaded && !IsFailed)
|
||||||
{
|
{
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
ServerState.Starting => "from-warning/20",
|
ServerState.Starting => "from-warning/20",
|
||||||
ServerState.Stopping => "from-warning/20",
|
ServerState.Stopping => "from-warning/20",
|
||||||
ServerState.Online => "from-success/20",
|
ServerState.Online => "from-success/20",
|
||||||
_ => "from-base-content/20"
|
_ => "from-base-100"
|
||||||
};
|
};
|
||||||
|
|
||||||
border = Status.State switch
|
border = Status.State switch
|
||||||
@@ -30,13 +30,13 @@
|
|||||||
ServerState.Starting => "border-warning",
|
ServerState.Starting => "border-warning",
|
||||||
ServerState.Stopping => "border-warning",
|
ServerState.Stopping => "border-warning",
|
||||||
ServerState.Online => "border-success",
|
ServerState.Online => "border-success",
|
||||||
_ => "border-base-content"
|
_ => "border-base-content/80"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<a href="/servers/@Server.Id"
|
<a href="/servers/@Server.Id"
|
||||||
class="w-full bg-gradient-to-r @gradient to-base-content/75 to-25% px-5 py-3.5 rounded-xl border-l-8 @border">
|
class="w-full bg-gradient-to-r @gradient to-base-100/75 to-25% px-5 py-3.5 rounded-xl border-l-8 @border">
|
||||||
<div class="grid grid-cols-6">
|
<div class="grid grid-cols-6">
|
||||||
<div class="flex items-center col-span-6 sm:col-span-2 2xl:col-span-1">
|
<div class="flex items-center col-span-6 sm:col-span-2 2xl:col-span-1">
|
||||||
<div class="bg-base-content/10 bg-opacity-45 py-1 px-2 rounded-lg flex items-center">
|
<div class="bg-base-content/10 bg-opacity-45 py-1 px-2 rounded-lg flex items-center">
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
Status.State is ServerState.Starting or ServerState.Stopping or ServerState.Online
|
Status.State is ServerState.Starting or ServerState.Stopping or ServerState.Online
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-cpu"></i>
|
<i class="icon-cpu"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<div class="ms-3">@(Stats.CpuUsage)%</div>
|
<div class="ms-3">@(Stats.CpuUsage)%</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-memory-stick"></i>
|
<i class="icon-memory-stick"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<div class="ms-3">@(Formatter.FormatSize(Stats.MemoryUsage)) / @(Formatter.FormatSize(ByteConverter.FromMegaBytes(Server.Memory).Bytes))</div>
|
<div class="ms-3">@(Formatter.FormatSize(Stats.MemoryUsage)) / @(Formatter.FormatSize(ByteConverter.FromMegaBytes(Server.Memory).Bytes))</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-hard-drive"></i>
|
<i class="icon-hard-drive"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
{
|
{
|
||||||
if (!IsLoaded)
|
if (!IsLoaded)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row text-gray-700">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row text-gray-700">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-loader"></i>
|
<i class="icon-loader"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
}
|
}
|
||||||
else if (IsFailed)
|
else if (IsFailed)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row text-error">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row text-error">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-cable"></i>
|
<i class="icon-cable"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
}
|
}
|
||||||
else if (IsLoaded && !IsFailed && Status.State is ServerState.Offline)
|
else if (IsLoaded && !IsFailed && Status.State is ServerState.Offline)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row text-error">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row text-error">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-power-off"></i>
|
<i class="icon-power-off"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
}
|
}
|
||||||
else if (IsLoaded && !IsFailed && Status.State is ServerState.Installing)
|
else if (IsLoaded && !IsFailed && Status.State is ServerState.Installing)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row text-primary">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row text-primary">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-hammer"></i>
|
<i class="icon-hammer"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
|
|
||||||
@if (Server.Share != null)
|
@if (Server.Share != null)
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row col-span-2">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row col-span-2">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-share-2"></i>
|
<i class="icon-share-2"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-sparkles"></i>
|
<i class="icon-sparkles"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
<div class="ms-3">@Server.StarName</div>
|
<div class="ms-3">@Server.StarName</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-base-content/35 py-1 px-2 rounded-lg flex flex-row">
|
<div class="bg-base-200/75 py-1 px-2 rounded-lg flex flex-row">
|
||||||
<div>
|
<div>
|
||||||
<i class="icon-database"></i>
|
<i class="icon-database"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
@using Microsoft.AspNetCore.SignalR.Client
|
@using Microsoft.AspNetCore.SignalR.Client
|
||||||
|
@using MoonlightServers.Frontend.Services
|
||||||
|
|
||||||
@inherits BaseServerTab
|
@inherits BaseServerTab
|
||||||
|
|
||||||
|
@inject ServerService ServerService
|
||||||
|
|
||||||
<div class="h-44">
|
<div class="h-44">
|
||||||
<XtermConsole @ref="XtermConsole" OnAfterInitialized="OnAfterConsoleInitialized"/>
|
<XtermConsole @ref="XtermConsole"
|
||||||
|
OnAfterInitialized="OnAfterConsoleInitialized"
|
||||||
|
CommandHistory="Parent.CommandHistory"
|
||||||
|
OnCommand="OnCommand"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
@@ -16,7 +22,7 @@
|
|||||||
|
|
||||||
HubConnection.On<string>("ConsoleOutput", async content =>
|
HubConnection.On<string>("ConsoleOutput", async content =>
|
||||||
{
|
{
|
||||||
if(XtermConsole != null)
|
if (XtermConsole != null)
|
||||||
await XtermConsole.Write(content);
|
await XtermConsole.Write(content);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,4 +33,7 @@
|
|||||||
{
|
{
|
||||||
await XtermConsole!.Write(InitialConsoleMessage);
|
await XtermConsole!.Write(InitialConsoleMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnCommand(string command)
|
||||||
|
=> await ServerService.RunCommand(Server.Id, command + "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<WButton CssClasses="btn btn-primary" OnClick="Reinstall">
|
<WButton CssClasses="btn btn-primary" OnClick="Reinstall">
|
||||||
<i class="align-middle icon-hammer me-1"></i>
|
<i class="align-middle icon-hammer"></i>
|
||||||
<span class="align-middle">Reinstall</span>
|
<span class="align-middle">Reinstall</span>
|
||||||
</WButton>
|
</WButton>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<div class="grid grid-col-1 gap-y-3">
|
<div class="grid grid-col-1 gap-y-3">
|
||||||
@foreach (var share in Shares)
|
@foreach (var share in Shares)
|
||||||
{
|
{
|
||||||
<div class="col-span-1 card card-body px-5 py-3 flex flex-row items-center justify-between">
|
<div class="col-span-1 card card-body py-3 flex flex-row items-center justify-between">
|
||||||
<div class="flex justify-start font-semibold">
|
<div class="flex justify-start font-semibold">
|
||||||
<i class="icon-user-round me-2"></i>
|
<i class="icon-user-round me-2"></i>
|
||||||
<span>@share.Username</span>
|
<span>@share.Username</span>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
||||||
@foreach (var variable in Variables)
|
@foreach (var variable in Variables)
|
||||||
{
|
{
|
||||||
<div class="sm:col-span-2 card card-body p-5">
|
<div class="sm:col-span-2 card card-body">
|
||||||
<label class="block text-sm font-medium leading-6 text-base-content">
|
<label class="block text-sm font-medium leading-6 text-base-content">
|
||||||
@variable.Name
|
@variable.Name
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.Servers
|
@using MoonlightServers.Shared.Http.Responses.Admin.Servers
|
||||||
@using MoonCore.Blazor.FlyonUi.Forms
|
@using MoonCore.Blazor.FlyonUi.Forms
|
||||||
|
@using MoonlightServers.Frontend.UI.Views.Admin.All
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
<label class="block text-sm font-medium leading-6 text-base-content">Allocations</label>
|
<label class="block text-sm font-medium leading-6 text-base-content">Allocations</label>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<InputMultipleItem TItem="NodeAllocationResponse"
|
<InputMultipleItem TItem="NodeAllocationResponse"
|
||||||
Value="Allocations"
|
Value="Parent.Allocations"
|
||||||
DisplayField="@(x => $"{x.IpAddress}:{x.Port}")"
|
DisplayField="@(x => $"{x.IpAddress}:{x.Port}")"
|
||||||
SearchField="@(x => $"{x.IpAddress}:{x.Port}")"
|
SearchField="@(x => $"{x.IpAddress}:{x.Port}")"
|
||||||
ItemSource="Loader">
|
ItemSource="Loader">
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
{
|
{
|
||||||
[Parameter] public UpdateServerRequest Request { get; set; }
|
[Parameter] public UpdateServerRequest Request { get; set; }
|
||||||
[Parameter] public ServerResponse Server { get; set; }
|
[Parameter] public ServerResponse Server { get; set; }
|
||||||
[Parameter] public List<NodeAllocationResponse> Allocations { get; set; }
|
[Parameter] public Update Parent { get; set; }
|
||||||
|
|
||||||
private async Task<NodeAllocationResponse[]> Loader()
|
private async Task<NodeAllocationResponse[]> Loader()
|
||||||
{
|
{
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Users
|
@using Moonlight.Shared.Http.Responses.Admin.Users
|
||||||
@using MoonCore.Blazor.FlyonUi.Forms
|
@using MoonCore.Blazor.FlyonUi.Forms
|
||||||
|
@using MoonlightServers.Frontend.UI.Views.Admin.All
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<InputItem TItem="UserResponse"
|
<InputItem TItem="UserResponse"
|
||||||
DisplayField="@(x => x.Username)"
|
DisplayField="@(x => x.Username)"
|
||||||
SearchField="@(x => x.Username)"
|
SearchField="@(x => x.Username)"
|
||||||
@bind-Value="Owner"
|
@bind-Value="Parent.Owner"
|
||||||
ItemSource="Loader">
|
ItemSource="Loader">
|
||||||
</InputItem>
|
</InputItem>
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[Parameter] public UpdateServerRequest Request { get; set; }
|
[Parameter] public UpdateServerRequest Request { get; set; }
|
||||||
[Parameter] public UserResponse Owner { get; set; }
|
[Parameter] public Update Parent { get; set; }
|
||||||
|
|
||||||
private async Task<UserResponse[]> Loader()
|
private async Task<UserResponse[]> Loader()
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
||||||
@foreach (var variable in Variables)
|
@foreach (var variable in ServerVariables)
|
||||||
{
|
{
|
||||||
var reqVariable = Request.Variables.FirstOrDefault(x => x.Key == variable.Key);
|
var reqVariable = Request.Variables.FirstOrDefault(x => x.Key == variable.Key);
|
||||||
var starVariable = StarVariables.FirstOrDefault(x => x.Key == variable.Key);
|
var starVariable = StarVariables.FirstOrDefault(x => x.Key == variable.Key);
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
[Parameter] public ServerResponse Server { get; set; }
|
[Parameter] public ServerResponse Server { get; set; }
|
||||||
|
|
||||||
private StarVariableDetailResponse[] StarVariables;
|
private StarVariableDetailResponse[] StarVariables;
|
||||||
private ServerVariableResponse[] Variables;
|
private ServerVariableResponse[] ServerVariables;
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task Load(LazyLoader _)
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Variables = await PagedData<ServerVariableResponse>.All(async (page, pageSize) =>
|
ServerVariables = await PagedData<ServerVariableResponse>.All(async (page, pageSize) =>
|
||||||
await ApiClient.GetJson<PagedData<ServerVariableResponse>>(
|
await ApiClient.GetJson<PagedData<ServerVariableResponse>>(
|
||||||
$"api/admin/servers/{Server.Id}/variables?page={page}&pageSize={pageSize}"
|
$"api/admin/servers/{Server.Id}/variables?page={page}&pageSize={pageSize}"
|
||||||
)
|
)
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
@using MoonlightServers.Shared.Http.Requests.Admin.Stars
|
@using MoonlightServers.Shared.Http.Requests.Admin.Stars
|
||||||
@using MoonlightServers.Shared.Models
|
@using MoonlightServers.Shared.Models
|
||||||
|
|
||||||
@inject ILogger<ParseConfigStarUpdate> Logger
|
@inject ILogger<ParseConfig> Logger
|
||||||
@inject ModalService ModalService
|
@inject ModalService ModalService
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
<div class="grid sm:grid-cols-2 xl:grid-cols-3 gap-4">
|
<div class="grid sm:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
@foreach (var variable in Variables)
|
@foreach (var variable in CurrentVariables)
|
||||||
{
|
{
|
||||||
<div class="col-span-1 card card-body p-2.5">
|
<div class="col-span-1 card card-body p-2.5">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
{
|
{
|
||||||
[Parameter] public StarDetailResponse Star { get; set; }
|
[Parameter] public StarDetailResponse Star { get; set; }
|
||||||
|
|
||||||
private StarVariableDetailResponse[] Variables;
|
private StarVariableDetailResponse[] CurrentVariables;
|
||||||
private LazyLoader LazyLoader;
|
private LazyLoader LazyLoader;
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
private async Task Load(LazyLoader arg)
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
$"api/admin/servers/stars/{Star.Id}/variables?page=0&pageSize=50"
|
$"api/admin/servers/stars/{Star.Id}/variables?page=0&pageSize=50"
|
||||||
);
|
);
|
||||||
|
|
||||||
Variables = pagedVariables.Items;
|
CurrentVariables = pagedVariables.Items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddVariable()
|
private async Task AddVariable()
|
||||||
@@ -1,31 +1,72 @@
|
|||||||
|
@using System.Collections.Concurrent
|
||||||
@using Microsoft.Extensions.Logging
|
@using Microsoft.Extensions.Logging
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Modals
|
||||||
|
@using MoonCore.Helpers
|
||||||
@using XtermBlazor
|
@using XtermBlazor
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Components
|
||||||
|
|
||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
|
@inject ModalService ModalService
|
||||||
@inject ILogger<XtermConsole> Logger
|
@inject ILogger<XtermConsole> Logger
|
||||||
|
|
||||||
<div class="bg-background rounded-lg p-2">
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
|
<div class="bg-black rounded-lg p-2 relative">
|
||||||
@if (IsInitialized)
|
@if (IsInitialized)
|
||||||
{
|
{
|
||||||
<Xterm @ref="Terminal"
|
<Xterm @ref="Terminal"
|
||||||
Addons="Addons"
|
Addons="Addons"
|
||||||
Options="Options"
|
Options="Options"
|
||||||
|
Class="h-full w-full"
|
||||||
OnFirstRender="HandleFirstRender"/>
|
OnFirstRender="HandleFirstRender"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="flex flex-row w-full mt-1.5">
|
||||||
|
<input @bind="CommandInput" @onkeyup="HandleKey" type="text" placeholder="Type here..." class="input grow"/>
|
||||||
|
<WButton OnClick="_ => SubmitCommand()" CssClasses="btn btn-square btn-primary grow-0 ms-1.5">
|
||||||
|
<i class="icon-send-horizontal text-lg"></i>
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="absolute top-4 right-4">
|
||||||
|
<div class="flex flex-col gap-y-1.5">
|
||||||
|
@if (IsPaused)
|
||||||
|
{
|
||||||
|
<button @onclick="TogglePause" class="btn btn-primary btn-square">
|
||||||
|
<i class="icon-play text-lg"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button @onclick="TogglePause" class="btn btn-secondary btn-square">
|
||||||
|
<i class="icon-pause text-lg"></i>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<button @onclick="OpenFullscreen" class="btn btn-secondary btn-square">
|
||||||
|
<i class="icon-maximize text-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[Parameter] public Func<Task>? OnAfterInitialized { get; set; }
|
[Parameter] public Func<Task>? OnAfterInitialized { get; set; }
|
||||||
[Parameter] public Func<Task>? OnFirstRender { get; set; }
|
[Parameter] public Func<Task>? OnFirstRender { get; set; }
|
||||||
|
[Parameter] public bool ShowActions { get; set; } = true;
|
||||||
|
[Parameter] public bool ShowInput { get; set; } = false;
|
||||||
|
[Parameter] public int MaxOutputCacheSize { get; set; } = 250;
|
||||||
|
[Parameter] public Func<string, Task>? OnCommand { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public IList<string> CommandHistory { get; set; } = new ConcurrentList<string>();
|
||||||
|
|
||||||
|
public event Func<string, Task>? OnWrite;
|
||||||
|
|
||||||
private Xterm Terminal;
|
private Xterm Terminal;
|
||||||
private bool IsInitialized = false;
|
public HashSet<string> Addons { get; } = ["addon-fit"];
|
||||||
private bool HadFirstRender = false;
|
|
||||||
|
|
||||||
private readonly Queue<string> MessageCache = new();
|
public TerminalOptions Options { get; } = new()
|
||||||
private readonly HashSet<string> Addons = ["addon-fit"];
|
|
||||||
private readonly TerminalOptions Options = new()
|
|
||||||
{
|
{
|
||||||
CursorBlink = false,
|
CursorBlink = false,
|
||||||
CursorStyle = CursorStyle.Bar,
|
CursorStyle = CursorStyle.Bar,
|
||||||
@@ -36,17 +77,23 @@
|
|||||||
Theme =
|
Theme =
|
||||||
{
|
{
|
||||||
Background = "#000000"
|
Background = "#000000"
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public ConcurrentList<string> OutputCache { get; private set; } = new();
|
||||||
|
public ConcurrentList<string> WriteQueue { get; private set; } = new();
|
||||||
|
private int CommandIndex = -1;
|
||||||
|
private bool IsReadyToWrite = false;
|
||||||
|
private bool IsPaused = false;
|
||||||
|
private bool IsInitialized = false;
|
||||||
|
|
||||||
|
private string CommandInput = "";
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if(!firstRender)
|
if (!firstRender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Initialize addons
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlightServers.loadAddons");
|
await JsRuntime.InvokeVoidAsync("moonlightServers.loadAddons");
|
||||||
@@ -59,7 +106,7 @@
|
|||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
if(OnAfterInitialized != null)
|
if (OnAfterInitialized != null)
|
||||||
await OnAfterInitialized.Invoke();
|
await OnAfterInitialized.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +121,13 @@
|
|||||||
Logger.LogError("An error occured while calling addons: {e}", e);
|
Logger.LogError("An error occured while calling addons: {e}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
HadFirstRender = true;
|
IsReadyToWrite = true;
|
||||||
|
|
||||||
// Write out cache
|
// Write queued content since initialisation started
|
||||||
while (MessageCache.Count > 0)
|
var queueContent = string.Concat(WriteQueue);
|
||||||
{
|
WriteQueue.Clear();
|
||||||
await Terminal.Write(MessageCache.Dequeue());
|
|
||||||
}
|
await Terminal.Write(queueContent);
|
||||||
|
|
||||||
if (OnFirstRender != null)
|
if (OnFirstRender != null)
|
||||||
await OnFirstRender.Invoke();
|
await OnFirstRender.Invoke();
|
||||||
@@ -90,9 +137,102 @@
|
|||||||
{
|
{
|
||||||
// We cache messages here as there is the chance that the console isn't ready for input while receiving write tasks
|
// We cache messages here as there is the chance that the console isn't ready for input while receiving write tasks
|
||||||
|
|
||||||
if (HadFirstRender)
|
if (IsReadyToWrite && !IsPaused)
|
||||||
await Terminal.Write(content);
|
await HandleWrite(content);
|
||||||
else
|
else
|
||||||
MessageCache.Enqueue(content);
|
WriteQueue.Add(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleWrite(string content)
|
||||||
|
{
|
||||||
|
// Update output cache and prune it if required
|
||||||
|
if (OutputCache.Count > MaxOutputCacheSize)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 50; i++)
|
||||||
|
OutputCache.RemoveAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputCache.Add(content);
|
||||||
|
|
||||||
|
// Trigger events
|
||||||
|
if (OnWrite != null)
|
||||||
|
await OnWrite.Invoke(content);
|
||||||
|
|
||||||
|
// Write in own terminal
|
||||||
|
await Terminal.Write(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenFullscreen()
|
||||||
|
{
|
||||||
|
await ModalService.Launch<FullScreenModal>(parameters => { parameters["Parent"] = this; }, size: "max-w-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TogglePause()
|
||||||
|
{
|
||||||
|
IsPaused = !IsPaused;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
if (IsPaused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var queueContent = string.Concat(WriteQueue);
|
||||||
|
WriteQueue.Clear();
|
||||||
|
|
||||||
|
await HandleWrite(queueContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SubmitCommand()
|
||||||
|
{
|
||||||
|
CommandHistory.Add(CommandInput);
|
||||||
|
|
||||||
|
if (OnCommand != null)
|
||||||
|
await OnCommand.Invoke(CommandInput);
|
||||||
|
|
||||||
|
CommandIndex = -1;
|
||||||
|
CommandInput = "";
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleKey(KeyboardEventArgs keyboard)
|
||||||
|
{
|
||||||
|
switch (keyboard.Code)
|
||||||
|
{
|
||||||
|
case "Enter":
|
||||||
|
await SubmitCommand();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ArrowUp" or "ArrowDown":
|
||||||
|
{
|
||||||
|
var highestIndex = CommandHistory.Count - 1;
|
||||||
|
|
||||||
|
if (CommandIndex == -1)
|
||||||
|
CommandIndex = highestIndex;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (keyboard.Code is "ArrowUp")
|
||||||
|
CommandIndex++;
|
||||||
|
else if (keyboard.Code is "ArrowDown")
|
||||||
|
CommandIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CommandIndex > highestIndex)
|
||||||
|
CommandIndex = highestIndex;
|
||||||
|
|
||||||
|
if (CommandIndex < 0)
|
||||||
|
CommandIndex = 0;
|
||||||
|
|
||||||
|
if (CommandIndex <= highestIndex || CommandHistory.Count > 0)
|
||||||
|
CommandInput = CommandHistory[CommandIndex];
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await Terminal.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Users
|
@using Moonlight.Shared.Http.Responses.Admin.Users
|
||||||
@using MoonlightServers.Shared.Http.Requests.Admin.Servers
|
@using MoonlightServers.Shared.Http.Requests.Admin.Servers
|
||||||
@using MoonlightServers.Frontend.UI.Components.Servers.CreateServerPartials
|
@using MoonlightServers.Frontend.UI.Components.Servers.CreatePartials
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
|
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.Stars
|
@using MoonlightServers.Shared.Http.Responses.Admin.Stars
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
<PageHeader Title="Create Server">
|
<PageHeader Title="Create Server">
|
||||||
<a href="/admin/servers/all" class="btn btn-secondary">
|
<a href="/admin/servers/all" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
<HandleForm @ref="Form" Model="Request" OnValidSubmit="OnSubmit">
|
<HandleForm @ref="Form" Model="Request" OnValidSubmit="OnSubmit">
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab Name="General">
|
<Tab Name="General">
|
||||||
<GeneralServerCreate Request="Request" Parent="this" />
|
<General Request="Request" Parent="this" />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Allocations">
|
<Tab Name="Allocations">
|
||||||
<AllocationsServerCreate Request="Request" Parent="this" />
|
<Allocations Request="Request" Parent="this" />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Variables">
|
<Tab Name="Variables">
|
||||||
<VariablesServerCreate Request="Request" Parent="this" />
|
<Variables Request="Request" Parent="this" />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Advanced">
|
<Tab Name="Advanced">
|
||||||
<AdvancedServerCreate Request="Request" />
|
<Advanced Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</HandleForm>
|
</HandleForm>
|
||||||
|
|||||||
@@ -33,7 +33,13 @@
|
|||||||
<Pagination TItem="ServerResponse" ItemSource="LoadData" />
|
<Pagination TItem="ServerResponse" ItemSource="LoadData" />
|
||||||
|
|
||||||
<DataTableColumn TItem="ServerResponse" Field="@(x => x.Id)" Name="Id"/>
|
<DataTableColumn TItem="ServerResponse" Field="@(x => x.Id)" Name="Id"/>
|
||||||
<DataTableColumn TItem="ServerResponse" Field="@(x => x.Name)" Name="Name"/>
|
<DataTableColumn TItem="ServerResponse" Field="@(x => x.Name)" Name="Name">
|
||||||
|
<ColumnTemplate>
|
||||||
|
<a class="text-primary" href="/admin/servers/all/update/@context.Id">
|
||||||
|
@context.Name
|
||||||
|
</a>
|
||||||
|
</ColumnTemplate>
|
||||||
|
</DataTableColumn>
|
||||||
<DataTableColumn TItem="ServerResponse" Field="@(x => x.NodeId)" Name="Node">
|
<DataTableColumn TItem="ServerResponse" Field="@(x => x.NodeId)" Name="Node">
|
||||||
<ColumnTemplate>
|
<ColumnTemplate>
|
||||||
@{
|
@{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
@using Moonlight.Shared.Http.Responses.Admin.Users
|
@using Moonlight.Shared.Http.Responses.Admin.Users
|
||||||
@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
|
||||||
@using MoonlightServers.Frontend.UI.Components.Servers.UpdateServerPartials
|
@using MoonlightServers.Frontend.UI.Components.Servers.UpdatePartials
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<PageHeader Title="Update Server">
|
<PageHeader Title="Update Server">
|
||||||
<a href="/admin/servers/all" class="btn btn-secondary">
|
<a href="/admin/servers/all" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
@@ -31,16 +31,16 @@
|
|||||||
<HandleForm @ref="Form" Model="Request" OnValidSubmit="OnSubmit">
|
<HandleForm @ref="Form" Model="Request" OnValidSubmit="OnSubmit">
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab Name="General">
|
<Tab Name="General">
|
||||||
<GeneralServerUpdate Request="Request" Owner="Owner"/>
|
<General Request="Request" Parent="this"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Allocations">
|
<Tab Name="Allocations">
|
||||||
<AllocationsServerUpdate Request="Request" Server="Server" Allocations="Allocations"/>
|
<Allocations Request="Request" Server="Server" Parent="this"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Variables">
|
<Tab Name="Variables">
|
||||||
<VariablesServerUpdate Request="Request" Server="Server"/>
|
<Variables Request="Request" Server="Server"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab Name="Advanced">
|
<Tab Name="Advanced">
|
||||||
<AdvancedServerUpdate Request="Request"/>
|
<Advanced Request="Request"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</HandleForm>
|
</HandleForm>
|
||||||
@@ -55,8 +55,8 @@
|
|||||||
private UpdateServerRequest Request;
|
private UpdateServerRequest Request;
|
||||||
private ServerResponse Server;
|
private ServerResponse Server;
|
||||||
|
|
||||||
private List<NodeAllocationResponse> Allocations = new();
|
public List<NodeAllocationResponse> Allocations = new();
|
||||||
private UserResponse Owner;
|
public UserResponse Owner;
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task Load(LazyLoader _)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
|
|
||||||
<PageHeader Title="Create Node">
|
<PageHeader Title="Create Node">
|
||||||
<a href="/admin/servers/nodes" class="btn btn-secondary">
|
<a href="/admin/servers/nodes" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonlightServers.Shared.Http.Requests.Admin.Nodes
|
@using MoonlightServers.Shared.Http.Requests.Admin.Nodes
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
|
@using MoonlightServers.Shared.Http.Responses.Admin.Nodes
|
||||||
@using MoonlightServers.Frontend.UI.Components.Nodes.UpdateNodePartials
|
@using MoonlightServers.Frontend.UI.Components.Nodes.UpdatePartials
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<PageHeader Title="@Node.Name">
|
<PageHeader Title="@Node.Name">
|
||||||
<a href="/admin/servers/nodes" class="btn btn-secondary">
|
<a href="/admin/servers/nodes" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
@@ -30,19 +30,19 @@
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab Name="Overview">
|
<Tab Name="Overview">
|
||||||
<OverviewNodeUpdate Node="Node" />
|
<Overview Node="Node" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Settings">
|
<Tab Name="Settings">
|
||||||
<GeneralNodeUpdate Request="Request"/>
|
<General Request="Request"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Allocations">
|
<Tab Name="Allocations">
|
||||||
<AllocationsNodeUpdate Node="Node"/>
|
<Allocations Node="Node"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Advanced Settings">
|
<Tab Name="Advanced Settings">
|
||||||
<AdvancedNodeUpdate Request="Request"/>
|
<Advanced Request="Request"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
|
|
||||||
<PageHeader Title="Create Star">
|
<PageHeader Title="Create Star">
|
||||||
<a href="/admin/servers/stars" class="btn btn-secondary">
|
<a href="/admin/servers/stars" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<PageHeader Title="Stars">
|
<PageHeader Title="Stars">
|
||||||
<InputFile id="import-file" hidden="" multiple OnChange="OnImportFiles"/>
|
<InputFile id="import-file" hidden="" multiple OnChange="OnImportFiles"/>
|
||||||
<label for="import-file" class="btn btn-accent cursor-pointer">
|
<label for="import-file" class="btn btn-accent cursor-pointer">
|
||||||
<i class="icon-file-up mr-2"></i>
|
<i class="icon-file-up"></i>
|
||||||
Import
|
Import
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonlightServers.Shared.Http.Requests.Admin.Stars
|
@using MoonlightServers.Shared.Http.Requests.Admin.Stars
|
||||||
@using MoonlightServers.Shared.Http.Responses.Admin.Stars
|
@using MoonlightServers.Shared.Http.Responses.Admin.Stars
|
||||||
@using MoonlightServers.Frontend.UI.Components.Stars.UpdateStarPartials
|
@using MoonlightServers.Frontend.UI.Components.Stars.UpdatePartials
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<PageHeader Title="Update Star">
|
<PageHeader Title="Update Star">
|
||||||
<a href="/admin/servers/stars" class="btn btn-secondary">
|
<a href="/admin/servers/stars" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left mr-1"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check mr-1"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
@@ -30,31 +30,31 @@
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab Name="General">
|
<Tab Name="General">
|
||||||
<GeneralStarUpdate Request="Request" />
|
<General Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Start, Stop & Status">
|
<Tab Name="Start, Stop & Status">
|
||||||
<StartStopStatusStarUpdate Request="Request" />
|
<StartStopStatus Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Parse Configuration">
|
<Tab Name="Parse Configuration">
|
||||||
<ParseConfigStarUpdate Request="Request" />
|
<ParseConfig Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Installation">
|
<Tab Name="Installation">
|
||||||
<InstallationStarUpdate Request="Request" />
|
<Installation Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Variables">
|
<Tab Name="Variables">
|
||||||
<VariablesStarUpdate Star="Detail" />
|
<Variables Star="Detail" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Docker Images">
|
<Tab Name="Docker Images">
|
||||||
<DockerImageStarUpdate Star="Detail" />
|
<DockerImage Star="Detail" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab Name="Miscellaneous">
|
<Tab Name="Miscellaneous">
|
||||||
<MiscStarUpdate Star="Detail" Request="Request" />
|
<Misc Star="Detail" Request="Request" />
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
|||||||
@@ -64,14 +64,14 @@
|
|||||||
@if (State == ServerState.Offline)
|
@if (State == ServerState.Offline)
|
||||||
{
|
{
|
||||||
<WButton CssClasses="btn btn-success" OnClick="_ => Start()">
|
<WButton CssClasses="btn btn-success" OnClick="_ => Start()">
|
||||||
<i class="icon-play me-1 align-middle"></i>
|
<i class="icon-play align-middle"></i>
|
||||||
<span class="align-middle">Start</span>
|
<span class="align-middle">Start</span>
|
||||||
</WButton>
|
</WButton>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" disabled="disabled">
|
<button type="button" class="btn btn-success" disabled="disabled">
|
||||||
<i class="icon-play me-1 align-middle"></i>
|
<i class="icon-play align-middle"></i>
|
||||||
<span class="align-middle">Start</span>
|
<span class="align-middle">Start</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -79,14 +79,14 @@
|
|||||||
@if (State == ServerState.Online)
|
@if (State == ServerState.Online)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary">
|
<button type="button" class="btn btn-primary">
|
||||||
<i class="icon-rotate-ccw me-1 align-middle"></i>
|
<i class="icon-rotate-ccw align-middle"></i>
|
||||||
<span class="align-middle">Restart</span>
|
<span class="align-middle">Restart</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-primary" disabled="disabled">
|
<button type="button" class="btn btn-primary" disabled="disabled">
|
||||||
<i class="icon-rotate-ccw me-1 align-middle"></i>
|
<i class="icon-rotate-ccw align-middle"></i>
|
||||||
<span class="align-middle">Restart</span>
|
<span class="align-middle">Restart</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -96,14 +96,14 @@
|
|||||||
if (State == ServerState.Stopping)
|
if (State == ServerState.Stopping)
|
||||||
{
|
{
|
||||||
<WButton CssClasses="btn btn-error" OnClick="_ => Kill()">
|
<WButton CssClasses="btn btn-error" OnClick="_ => Kill()">
|
||||||
<i class="icon-bomb me-1 align-middle"></i>
|
<i class="icon-bomb align-middle"></i>
|
||||||
<span class="align-middle">Kill</span>
|
<span class="align-middle">Kill</span>
|
||||||
</WButton>
|
</WButton>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<WButton CssClasses="btn btn-error" OnClick="_ => Stop()">
|
<WButton CssClasses="btn btn-error" OnClick="_ => Stop()">
|
||||||
<i class="icon-squircle me-1 align-middle"></i>
|
<i class="icon-squircle align-middle"></i>
|
||||||
<span class="align-middle">Stop</span>
|
<span class="align-middle">Stop</span>
|
||||||
</WButton>
|
</WButton>
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-error" disabled="disabled">
|
<button type="button" class="btn btn-error" disabled="disabled">
|
||||||
<i class="icon-squircle me-1 align-middle"></i>
|
<i class="icon-squircle align-middle"></i>
|
||||||
<span class="align-middle">Stop</span>
|
<span class="align-middle">Stop</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -119,17 +119,17 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" disabled="disabled">
|
<button type="button" class="btn btn-success" disabled="disabled">
|
||||||
<i class="icon-play me-1 align-middle"></i>
|
<i class="icon-play align-middle"></i>
|
||||||
<span class="align-middle">Start</span>
|
<span class="align-middle">Start</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" class="btn btn-primary" disabled="disabled">
|
<button type="button" class="btn btn-primary" disabled="disabled">
|
||||||
<i class="icon-rotate-ccw me-1 align-middle"></i>
|
<i class="icon-rotate-ccw align-middle"></i>
|
||||||
<span class="align-middle">Restart</span>
|
<span class="align-middle">Restart</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" class="btn btn-error" disabled="disabled">
|
<button type="button" class="btn btn-error" disabled="disabled">
|
||||||
<i class="icon-squircle me-1 align-middle"></i>
|
<i class="icon-squircle align-middle"></i>
|
||||||
<span class="align-middle">Stop</span>
|
<span class="align-middle">Stop</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -138,28 +138,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<ul class="flex flex-wrap -m-1">
|
|
||||||
|
<nav class="tabs space-x-2 overflow-x-auto" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
||||||
@foreach (var tab in Tabs)
|
@foreach (var tab in Tabs)
|
||||||
{
|
|
||||||
<li class="m-1">
|
|
||||||
@if (tab == CurrentTab)
|
|
||||||
{
|
{
|
||||||
<a href="/servers/@(ServerId)/@(tab.Path)"
|
<a href="/servers/@(ServerId)/@(tab.Path)"
|
||||||
class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-transparent shadow-sm bg-gray-100 text-gray-800 transition">
|
class="btn btn-text active-tab:bg-primary active-tab:text-primary-content hover:text-primary hover:bg-primary/20 bg-base-150 text-sm py-0.5 px-3 rounded-xl @(tab == CurrentTab ? "active" : "")"
|
||||||
|
data-tab="na"
|
||||||
|
role="tab"
|
||||||
|
@onclick:preventDefault
|
||||||
|
@onclick="() => SwitchTab(tab)">
|
||||||
@tab.Name
|
@tab.Name
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
else
|
</nav>
|
||||||
{
|
|
||||||
<a href="/servers/@(ServerId)/@(tab.Path)" @onclick:preventDefault
|
|
||||||
@onclick="() => SwitchTab(tab)"
|
|
||||||
class="inline-flex items-center justify-center text-sm font-medium leading-5 rounded-full px-3 py-1 border border-gray-700/60 hover:border-gray-600 shadow-sm bg-gray-800 text-base-content/60 transition">
|
|
||||||
@tab.Name
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
@if (CurrentTab == null)
|
@if (CurrentTab == null)
|
||||||
@@ -201,6 +193,8 @@
|
|||||||
|
|
||||||
private HubConnection? HubConnection;
|
private HubConnection? HubConnection;
|
||||||
|
|
||||||
|
public ConcurrentList<string> CommandHistory = new();
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task Load(LazyLoader _)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Shared.Http.Requests.Client.Servers;
|
||||||
|
|
||||||
|
public class ServerCommandRequest
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "The command is required")]
|
||||||
|
public string Command { get; set; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user