mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-11-25 16:42:54 +01:00
Compare commits
3 Commits
e796a364f4
...
d9c187994b
Author | SHA1 | Date | |
---|---|---|---|
d9c187994b | |||
3a18d2067f | |||
149bb6e0f1 |
@ -17,6 +17,5 @@ public sealed partial record AgentWithStats(
|
||||
[property: MemoryPackOrder(9)] DateTimeOffset? LastPing,
|
||||
[property: MemoryPackOrder(10)] bool IsOnline
|
||||
) {
|
||||
[MemoryPackIgnore]
|
||||
public RamAllocationUnits? AvailableMemory => MaxMemory - Stats?.RunningInstanceMemory;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
namespace Phantom.Common.Data.Web.Instance;
|
||||
|
||||
public enum CreateOrUpdateInstanceResult : byte {
|
||||
UnknownError,
|
||||
Success,
|
||||
InstanceNameMustNotBeEmpty,
|
||||
InstanceMemoryMustNotBeZero,
|
||||
MinecraftVersionDownloadInfoNotFound,
|
||||
AgentNotFound
|
||||
}
|
||||
|
||||
public static class CreateOrUpdateInstanceResultExtensions {
|
||||
public static string ToSentence(this CreateOrUpdateInstanceResult reason) {
|
||||
return reason switch {
|
||||
CreateOrUpdateInstanceResult.Success => "Success.",
|
||||
CreateOrUpdateInstanceResult.InstanceNameMustNotBeEmpty => "Instance name must not be empty.",
|
||||
CreateOrUpdateInstanceResult.InstanceMemoryMustNotBeZero => "Memory must not be 0 MB.",
|
||||
CreateOrUpdateInstanceResult.MinecraftVersionDownloadInfoNotFound => "Could not find download information for the selected Minecraft version.",
|
||||
CreateOrUpdateInstanceResult.AgentNotFound => "Agent not found.",
|
||||
_ => "Unknown error."
|
||||
};
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
using MemoryPack;
|
||||
namespace Phantom.Common.Data.Minecraft;
|
||||
|
||||
namespace Phantom.Common.Data.Minecraft;
|
||||
|
||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||
public sealed partial record MinecraftVersion(
|
||||
[property: MemoryPackOrder(0)] string Id,
|
||||
[property: MemoryPackOrder(1)] MinecraftVersionType Type,
|
||||
[property: MemoryPackOrder(2)] string MetadataUrl
|
||||
public sealed record MinecraftVersion(
|
||||
string Id,
|
||||
MinecraftVersionType Type,
|
||||
string MetadataUrl
|
||||
);
|
||||
|
@ -1,7 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using Phantom.Common.Data.Java;
|
||||
using Phantom.Common.Data.Minecraft;
|
||||
using Phantom.Common.Data.Replies;
|
||||
using Phantom.Common.Data.Replies;
|
||||
using Phantom.Common.Data.Web.Instance;
|
||||
using Phantom.Common.Data.Web.Users;
|
||||
using Phantom.Common.Messages.Web.BiDirectional;
|
||||
@ -15,8 +12,5 @@ public interface IMessageToControllerListener {
|
||||
Task<LogInSuccess?> HandleLogIn(LogInMessage message);
|
||||
Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message);
|
||||
Task<InstanceActionResult<CreateOrUpdateInstanceResult>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message);
|
||||
Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message);
|
||||
Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message);
|
||||
Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> HandleGetAgentJavaRuntimes(GetAgentJavaRuntimes message);
|
||||
Task<NoReply> HandleReply(ReplyMessage message);
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using MemoryPack;
|
||||
using Phantom.Common.Data.Java;
|
||||
|
||||
namespace Phantom.Common.Messages.Web.ToController;
|
||||
|
||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||
public sealed partial record GetAgentJavaRuntimes : IMessageToController<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> {
|
||||
public Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> Accept(IMessageToControllerListener listener) {
|
||||
return listener.HandleGetAgentJavaRuntimes(this);
|
||||
}
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using MemoryPack;
|
||||
using Phantom.Common.Data.Minecraft;
|
||||
|
||||
namespace Phantom.Common.Messages.Web.ToController;
|
||||
|
||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||
public sealed partial record GetMinecraftVersionsMessage : IMessageToController<ImmutableArray<MinecraftVersion>> {
|
||||
public Task<ImmutableArray<MinecraftVersion>> Accept(IMessageToControllerListener listener) {
|
||||
return listener.HandleGetMinecraftVersions(this);
|
||||
}
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
using MemoryPack;
|
||||
using Phantom.Common.Data.Replies;
|
||||
|
||||
namespace Phantom.Common.Messages.Web.ToController;
|
||||
|
||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||
public sealed partial record LaunchInstanceMessage(
|
||||
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
||||
[property: MemoryPackOrder(1)] Guid InstanceGuid
|
||||
) : IMessageToController<InstanceActionResult<LaunchInstanceResult>> {
|
||||
public Task<InstanceActionResult<LaunchInstanceResult>> Accept(IMessageToControllerListener listener) {
|
||||
return listener.HandleLaunchInstance(this);
|
||||
}
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using Phantom.Common.Data.Java;
|
||||
using Phantom.Common.Data.Minecraft;
|
||||
using Phantom.Common.Data.Replies;
|
||||
using Phantom.Common.Data.Replies;
|
||||
using Phantom.Common.Data.Web.Instance;
|
||||
using Phantom.Common.Data.Web.Users;
|
||||
using Phantom.Common.Logging;
|
||||
@ -23,9 +20,6 @@ public static class WebMessageRegistries {
|
||||
ToController.Add<LogInMessage, LogInSuccess?>(1);
|
||||
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(2);
|
||||
ToController.Add<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(3);
|
||||
ToController.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(4);
|
||||
ToController.Add<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(5);
|
||||
ToController.Add<GetAgentJavaRuntimes, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(6);
|
||||
ToController.Add<ReplyMessage>(127);
|
||||
|
||||
ToWeb.Add<RegisterWebResultMessage>(0);
|
||||
|
@ -60,7 +60,7 @@ public sealed class ControllerServices {
|
||||
}
|
||||
|
||||
public WebMessageListener CreateWebMessageListener(RpcConnectionToClient<IMessageToWebListener> connection) {
|
||||
return new WebMessageListener(connection, webAuthToken, UserManager, UserLoginManager, AgentManager, AgentJavaRuntimesManager, InstanceManager, MinecraftVersions, TaskManager);
|
||||
return new WebMessageListener(connection, webAuthToken, UserManager, UserLoginManager, AgentManager, InstanceManager, TaskManager);
|
||||
}
|
||||
|
||||
public async Task Initialize() {
|
||||
|
@ -0,0 +1,23 @@
|
||||
namespace Phantom.Controller.Services.Instances;
|
||||
|
||||
public enum AddOrEditInstanceResult : byte {
|
||||
UnknownError,
|
||||
Success,
|
||||
InstanceNameMustNotBeEmpty,
|
||||
InstanceMemoryMustNotBeZero,
|
||||
MinecraftVersionDownloadInfoNotFound,
|
||||
AgentNotFound
|
||||
}
|
||||
|
||||
public static class AddOrEditInstanceResultExtensions {
|
||||
public static string ToSentence(this AddOrEditInstanceResult reason) {
|
||||
return reason switch {
|
||||
AddOrEditInstanceResult.Success => "Success.",
|
||||
AddOrEditInstanceResult.InstanceNameMustNotBeEmpty => "Instance name must not be empty.",
|
||||
AddOrEditInstanceResult.InstanceMemoryMustNotBeZero => "Memory must not be 0 MB.",
|
||||
AddOrEditInstanceResult.MinecraftVersionDownloadInfoNotFound => "Could not find download information for the selected Minecraft version.",
|
||||
AddOrEditInstanceResult.AgentNotFound => "Agent not found.",
|
||||
_ => "Unknown error."
|
||||
};
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@ sealed class InstanceManager {
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ConvertIfStatementToConditionalTernaryExpression")]
|
||||
public async Task<InstanceActionResult<CreateOrUpdateInstanceResult>> CreateOrUpdateInstance(Guid auditLogUserGuid, InstanceConfiguration configuration) {
|
||||
public async Task<InstanceActionResult<CreateOrUpdateInstanceResult>> CreateOrUpdateInstance(InstanceConfiguration configuration, Guid auditLogUserGuid) {
|
||||
var agent = agentManager.GetAgent(configuration.AgentGuid);
|
||||
if (agent == null) {
|
||||
return InstanceActionResult.Concrete(CreateOrUpdateInstanceResult.AgentNotFound);
|
||||
@ -176,38 +176,34 @@ sealed class InstanceManager {
|
||||
return instances.ByGuid.TryGetValue(instanceGuid, out var instance) ? await SendInstanceActionMessage<TMessage, TReply>(instance, message) : InstanceActionResult.General<TReply>(InstanceActionGeneralResult.InstanceDoesNotExist);
|
||||
}
|
||||
|
||||
public async Task<InstanceActionResult<LaunchInstanceResult>> LaunchInstance(Guid auditLogUserGuid, Guid instanceGuid) {
|
||||
public async Task<InstanceActionResult<LaunchInstanceResult>> LaunchInstance(Guid instanceGuid) {
|
||||
var result = await SendInstanceActionMessage<LaunchInstanceMessage, LaunchInstanceResult>(instanceGuid, new LaunchInstanceMessage(instanceGuid));
|
||||
if (result.Is(LaunchInstanceResult.LaunchInitiated)) {
|
||||
await HandleInstanceManuallyLaunchedOrStopped(instanceGuid, true, auditLogUserGuid, auditLogRepository => auditLogRepository.AddInstanceLaunchedEvent(instanceGuid));
|
||||
await SetInstanceShouldLaunchAutomatically(instanceGuid, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<InstanceActionResult<StopInstanceResult>> StopInstance(Guid auditLogUserGuid, Guid instanceGuid, MinecraftStopStrategy stopStrategy) {
|
||||
public async Task<InstanceActionResult<StopInstanceResult>> StopInstance(Guid instanceGuid, MinecraftStopStrategy stopStrategy) {
|
||||
var result = await SendInstanceActionMessage<StopInstanceMessage, StopInstanceResult>(instanceGuid, new StopInstanceMessage(instanceGuid, stopStrategy));
|
||||
if (result.Is(StopInstanceResult.StopInitiated)) {
|
||||
await HandleInstanceManuallyLaunchedOrStopped(instanceGuid, false, auditLogUserGuid, auditLogRepository => auditLogRepository.AddInstanceLaunchedEvent(instanceGuid));
|
||||
await SetInstanceShouldLaunchAutomatically(instanceGuid, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task HandleInstanceManuallyLaunchedOrStopped(Guid instanceGuid, bool wasLaunched, Guid auditLogUserGuid, Action<AuditLogRepository> addAuditEvent) {
|
||||
private async Task SetInstanceShouldLaunchAutomatically(Guid instanceGuid, bool shouldLaunchAutomatically) {
|
||||
await modifyInstancesSemaphore.WaitAsync(cancellationToken);
|
||||
try {
|
||||
instances.ByGuid.TryReplace(instanceGuid, instance => instance with { LaunchAutomatically = wasLaunched });
|
||||
instances.ByGuid.TryReplace(instanceGuid, instance => instance with { LaunchAutomatically = shouldLaunchAutomatically });
|
||||
|
||||
await using var db = dbProvider.Lazy();
|
||||
var entity = await db.Ctx.Instances.FindAsync(new object[] { instanceGuid }, cancellationToken);
|
||||
await using var ctx = dbProvider.Eager();
|
||||
var entity = await ctx.Instances.FindAsync(new object[] { instanceGuid }, cancellationToken);
|
||||
if (entity != null) {
|
||||
entity.LaunchAutomatically = wasLaunched;
|
||||
|
||||
var auditLogRepository = new AuditLogRepository(db, auditLogUserGuid);
|
||||
addAuditEvent(auditLogRepository);
|
||||
|
||||
await db.Ctx.SaveChangesAsync(cancellationToken);
|
||||
entity.LaunchAutomatically = shouldLaunchAutomatically;
|
||||
await ctx.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
} finally {
|
||||
modifyInstancesSemaphore.Release();
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System.Collections.Immutable;
|
||||
using Phantom.Common.Data;
|
||||
using Phantom.Common.Data.Java;
|
||||
using Phantom.Common.Data.Minecraft;
|
||||
using Phantom.Common.Data.Replies;
|
||||
using Phantom.Common.Data.Web.Agent;
|
||||
using Phantom.Common.Data.Web.Instance;
|
||||
@ -11,7 +9,6 @@ using Phantom.Common.Messages.Web;
|
||||
using Phantom.Common.Messages.Web.BiDirectional;
|
||||
using Phantom.Common.Messages.Web.ToController;
|
||||
using Phantom.Common.Messages.Web.ToWeb;
|
||||
using Phantom.Controller.Minecraft;
|
||||
using Phantom.Controller.Rpc;
|
||||
using Phantom.Controller.Services.Agents;
|
||||
using Phantom.Controller.Services.Instances;
|
||||
@ -30,20 +27,16 @@ public sealed class WebMessageListener : IMessageToControllerListener {
|
||||
private readonly UserManager userManager;
|
||||
private readonly UserLoginManager userLoginManager;
|
||||
private readonly AgentManager agentManager;
|
||||
private readonly AgentJavaRuntimesManager agentJavaRuntimesManager;
|
||||
private readonly InstanceManager instanceManager;
|
||||
private readonly MinecraftVersions minecraftVersions;
|
||||
private readonly TaskManager taskManager;
|
||||
|
||||
internal WebMessageListener(RpcConnectionToClient<IMessageToWebListener> connection, AuthToken authToken, UserManager userManager, UserLoginManager userLoginManager, AgentManager agentManager, AgentJavaRuntimesManager agentJavaRuntimesManager, InstanceManager instanceManager, MinecraftVersions minecraftVersions, TaskManager taskManager) {
|
||||
internal WebMessageListener(RpcConnectionToClient<IMessageToWebListener> connection, AuthToken authToken, UserManager userManager, UserLoginManager userLoginManager, AgentManager agentManager, InstanceManager instanceManager, TaskManager taskManager) {
|
||||
this.connection = connection;
|
||||
this.authToken = authToken;
|
||||
this.userManager = userManager;
|
||||
this.userLoginManager = userLoginManager;
|
||||
this.agentManager = agentManager;
|
||||
this.agentJavaRuntimesManager = agentJavaRuntimesManager;
|
||||
this.instanceManager = instanceManager;
|
||||
this.minecraftVersions = minecraftVersions;
|
||||
this.taskManager = taskManager;
|
||||
}
|
||||
|
||||
@ -79,19 +72,7 @@ public sealed class WebMessageListener : IMessageToControllerListener {
|
||||
}
|
||||
|
||||
public Task<InstanceActionResult<CreateOrUpdateInstanceResult>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
|
||||
return instanceManager.CreateOrUpdateInstance( message.LoggedInUserGuid, message.Configuration);
|
||||
}
|
||||
|
||||
public Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
||||
return instanceManager.LaunchInstance(message.LoggedInUserGuid, message.InstanceGuid);
|
||||
}
|
||||
|
||||
public Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
|
||||
return minecraftVersions.GetVersions(CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> HandleGetAgentJavaRuntimes(GetAgentJavaRuntimes message) {
|
||||
return Task.FromResult(agentJavaRuntimesManager.All);
|
||||
return instanceManager.CreateOrUpdateInstance(message.Configuration, message.LoggedInUserGuid);
|
||||
}
|
||||
|
||||
public Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
|
||||
|
@ -10,11 +10,11 @@ public sealed class AgentManager {
|
||||
|
||||
public EventSubscribers<ImmutableArray<AgentWithStats>> AgentsChanged => agents.Subs;
|
||||
|
||||
internal void RefreshAgents(ImmutableArray<AgentWithStats> newAgents) {
|
||||
agents.SetTo(newAgents);
|
||||
}
|
||||
|
||||
public ImmutableDictionary<Guid, AgentWithStats> ToDictionaryByGuid() {
|
||||
return agents.Value.ToImmutableDictionary(static agent => agent.Guid);
|
||||
}
|
||||
|
||||
internal void RefreshAgents(ImmutableArray<AgentWithStats> newAgents) {
|
||||
agents.SetTo(newAgents);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ using Phantom.Common.Data.Web.Users;
|
||||
|
||||
namespace Phantom.Web.Services.Authentication;
|
||||
|
||||
public sealed record UserInfo(Guid UserGuid, string Username, PermissionSet Permissions) {
|
||||
sealed record UserInfo(Guid UserGuid, string Username, PermissionSet Permissions) {
|
||||
private const string AuthenticationType = "Phantom";
|
||||
|
||||
internal ClaimsPrincipal AsClaimsPrincipal {
|
||||
public ClaimsPrincipal AsClaimsPrincipal {
|
||||
get {
|
||||
var identity = new ClaimsIdentity(AuthenticationType);
|
||||
|
||||
@ -18,6 +18,6 @@ public sealed record UserInfo(Guid UserGuid, string Username, PermissionSet Perm
|
||||
}
|
||||
|
||||
public static Guid? TryGetGuid(ClaimsPrincipal principal) {
|
||||
return principal.Identity is { IsAuthenticated: true, AuthenticationType: AuthenticationType } && principal.FindFirstValue(ClaimTypes.NameIdentifier) is {} guidStr && Guid.TryParse(guidStr, out var guid) ? guid : null;
|
||||
return principal.FindFirstValue(ClaimTypes.NameIdentifier) is {} guidStr && Guid.TryParse(guidStr, out var guid) ? guid : null;
|
||||
}
|
||||
}
|
||||
|
@ -9,33 +9,21 @@ using Phantom.Web.Services.Rpc;
|
||||
|
||||
namespace Phantom.Web.Services.Instances;
|
||||
|
||||
using InstanceDictionary = ImmutableDictionary<Guid, Instance>;
|
||||
|
||||
public sealed class InstanceManager {
|
||||
private readonly ControllerConnection controllerConnection;
|
||||
private readonly SimpleObservableState<InstanceDictionary> instances = new (PhantomLogger.Create<InstanceManager>("Instances"), InstanceDictionary.Empty);
|
||||
private readonly SimpleObservableState<ImmutableArray<Instance>> instances = new (PhantomLogger.Create<InstanceManager>("Instances"), ImmutableArray<Instance>.Empty);
|
||||
|
||||
public InstanceManager(ControllerConnection controllerConnection) {
|
||||
this.controllerConnection = controllerConnection;
|
||||
}
|
||||
|
||||
public EventSubscribers<InstanceDictionary> InstancesChanged => instances.Subs;
|
||||
public EventSubscribers<ImmutableArray<Instance>> InstancesChanged => instances.Subs;
|
||||
|
||||
internal void RefreshInstances(ImmutableArray<Instance> newInstances) {
|
||||
instances.SetTo(newInstances.ToImmutableDictionary(static instance => instance.Configuration.InstanceGuid));
|
||||
instances.SetTo(newInstances);
|
||||
}
|
||||
|
||||
public Instance? GetByGuid(Guid instanceGuid) {
|
||||
return instances.Value.GetValueOrDefault(instanceGuid);
|
||||
}
|
||||
|
||||
public Task<InstanceActionResult<CreateOrUpdateInstanceResult>> CreateOrUpdateInstance(Guid loggedInUserGuid, InstanceConfiguration configuration) {
|
||||
var message = new CreateOrUpdateInstanceMessage(loggedInUserGuid, configuration);
|
||||
return controllerConnection.Send<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(message, TimeSpan.FromSeconds(30));
|
||||
}
|
||||
|
||||
public Task<InstanceActionResult<LaunchInstanceResult>> LaunchInstance(Guid loggedInUserGuid, Guid instanceGuid) {
|
||||
var message = new LaunchInstanceMessage(loggedInUserGuid, instanceGuid);
|
||||
return controllerConnection.Send<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(message, TimeSpan.FromSeconds(30));
|
||||
public Task<InstanceActionResult<CreateOrUpdateInstanceResult>> CreateOrUpdateInstance(InstanceConfiguration configuration) {
|
||||
return controllerConnection.Send<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(new CreateOrUpdateInstanceMessage(configuration), TimeSpan.FromSeconds(30));
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ public sealed class ControllerConnection {
|
||||
return connection.Send(message);
|
||||
}
|
||||
|
||||
public Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken = default) where TMessage : IMessageToController<TReply> {
|
||||
return connection.Send<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken);
|
||||
public Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan timeout) where TMessage : IMessageToController<TReply> {
|
||||
return connection.Send<TMessage, TReply>(message, timeout, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using Phantom.Common.Data.Web.Users;
|
||||
using Phantom.Common.Logging;
|
||||
using Phantom.Web.Services.Authorization;
|
||||
using ILogger = Serilog.ILogger;
|
||||
using UserInfo = Phantom.Web.Services.Authentication.UserInfo;
|
||||
|
||||
namespace Phantom.Web.Base;
|
||||
|
||||
@ -17,11 +16,6 @@ public abstract class PhantomComponent : ComponentBase {
|
||||
[Inject]
|
||||
public PermissionManager PermissionManager { get; set; } = null!;
|
||||
|
||||
public async Task<Guid?> GetUserGuid() {
|
||||
var authenticationState = await AuthenticationStateTask;
|
||||
return UserInfo.TryGetGuid(authenticationState.User);
|
||||
}
|
||||
|
||||
protected async Task<bool> CheckPermission(Permission permission) {
|
||||
var authenticationState = await AuthenticationStateTask;
|
||||
return PermissionManager.CheckPermission(authenticationState.User, permission);
|
||||
|
@ -1,13 +1,12 @@
|
||||
@page "/instances/{InstanceGuid:guid}"
|
||||
@attribute [Authorize(Permission.ViewInstancesPolicy)]
|
||||
@inherits PhantomComponent
|
||||
@using Phantom.Common.Data.Instance
|
||||
@using Phantom.Common.Data.Replies
|
||||
@using Phantom.Common.Data.Web.Instance
|
||||
@using Phantom.Common.Data.Web.Users
|
||||
@using Phantom.Web.Services.Instances
|
||||
@using Phantom.Common.Data.Web.Users
|
||||
@using Phantom.Common.Data.Replies
|
||||
@implements IDisposable
|
||||
@inject InstanceManager InstanceManager
|
||||
@inject AuditLog AuditLog
|
||||
|
||||
@if (Instance == null) {
|
||||
<h1>Instance Not Found</h1>
|
||||
@ -46,7 +45,7 @@ else {
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public Guid InstanceGuid { get; init; }
|
||||
public Guid InstanceGuid { get; set; }
|
||||
|
||||
private string? lastError = null;
|
||||
private bool isLaunchingInstance = false;
|
||||
@ -64,11 +63,6 @@ else {
|
||||
}
|
||||
|
||||
private async Task LaunchInstance() {
|
||||
var loggedInUserGuid = await GetUserGuid();
|
||||
if (loggedInUserGuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
isLaunchingInstance = true;
|
||||
lastError = null;
|
||||
|
||||
@ -78,8 +72,11 @@ else {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await InstanceManager.LaunchInstance(loggedInUserGuid.Value, InstanceGuid);
|
||||
if (!result.Is(LaunchInstanceResult.LaunchInitiated)) {
|
||||
var result = await InstanceManager.LaunchInstance(InstanceGuid);
|
||||
if (result.Is(LaunchInstanceResult.LaunchInitiated)) {
|
||||
await AuditLog.AddInstanceLaunchedEvent(InstanceGuid);
|
||||
}
|
||||
else {
|
||||
lastError = result.ToSentence(Messages.ToSentence);
|
||||
}
|
||||
} finally {
|
||||
|
@ -18,12 +18,12 @@ else {
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public Guid InstanceGuid { get; init; }
|
||||
public Guid InstanceGuid { get; set; }
|
||||
|
||||
private InstanceConfiguration? InstanceConfiguration { get; set; }
|
||||
|
||||
protected override void OnInitialized() {
|
||||
InstanceConfiguration = InstanceManager.GetByGuid(InstanceGuid)?.Configuration;
|
||||
InstanceConfiguration = InstanceManager.GetInstanceConfiguration(InstanceGuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,8 +83,7 @@
|
||||
});
|
||||
|
||||
InstanceManager.InstancesChanged.Subscribe(this, instances => {
|
||||
this.instances = instances.Values
|
||||
.OrderBy(instance => agentNames.TryGetValue(instance.Configuration.AgentGuid, out var agentName) ? agentName : string.Empty)
|
||||
this.instances = instances.OrderBy(instance => agentNames.TryGetValue(instance.Configuration.AgentGuid, out var agentName) ? agentName : string.Empty)
|
||||
.ThenBy(static instance => instance.Configuration.InstanceName)
|
||||
.ToImmutableArray();
|
||||
InvokeAsync(StateHasChanged);
|
||||
|
@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
private async Task<Result<string>> CreateOrUpdateAdministrator() {
|
||||
var reply = await ControllerConnection.Send<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(new CreateOrUpdateAdministratorUserMessage(form.Username, form.Password), Timeout.InfiniteTimeSpan);
|
||||
var reply = await ControllerConnection.Send<CreateOrUpdateAdministratorUser, CreateOrUpdateAdministratorUserResult>(new CreateOrUpdateAdministratorUser(form.Username, form.Password), Timeout.InfiniteTimeSpan);
|
||||
return reply switch {
|
||||
Success => Result.Ok<string>(),
|
||||
CreationFailed fail => fail.Error.ToSentences("\n"),
|
||||
|
@ -1,31 +1,27 @@
|
||||
@using Phantom.Web.Components.Utils
|
||||
@using Phantom.Common.Data.Minecraft
|
||||
@using Phantom.Common.Data.Web.Minecraft
|
||||
@using Phantom.Common.Data.Instance
|
||||
@using Phantom.Common.Data.Java
|
||||
@using System.Collections.Immutable
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using System.Diagnostics.CodeAnalysis
|
||||
@using Phantom.Common.Data.Minecraft
|
||||
@using Phantom.Common.Data.Web.Agent
|
||||
@using Phantom.Common.Data.Web.Instance
|
||||
@using Phantom.Common.Data.Web.Minecraft
|
||||
@using Phantom.Common.Messages.Web.ToController
|
||||
@using Phantom.Common.Data.Instance
|
||||
@using Phantom.Common.Data.Java
|
||||
@using Phantom.Common.Data
|
||||
@using Phantom.Web.Services
|
||||
@using Phantom.Web.Services.Agents
|
||||
@using Phantom.Web.Services.Instances
|
||||
@using Phantom.Web.Services.Rpc
|
||||
@inherits PhantomComponent
|
||||
@inject INavigation Nav
|
||||
@inject ControllerConnection ControllerConnection
|
||||
@inject MinecraftVersions MinecraftVersions
|
||||
@inject AgentManager AgentManager
|
||||
@inject AgentJavaRuntimesManager AgentJavaRuntimesManager
|
||||
@inject InstanceManager InstanceManager
|
||||
@inject AuditLog AuditLog
|
||||
|
||||
<Form Model="form" OnSubmit="AddOrEditInstance">
|
||||
@{ var selectedAgent = form.SelectedAgent; }
|
||||
<div class="row">
|
||||
<div class="col-xl-7 mb-3">
|
||||
@{
|
||||
static RenderFragment GetAgentOption(AgentWithStats agent) {
|
||||
static RenderFragment GetAgentOption(Agent agent) {
|
||||
return @<option value="@agent.Guid">
|
||||
@agent.Name
|
||||
•
|
||||
@ -38,14 +34,14 @@
|
||||
@if (EditedInstanceConfiguration == null) {
|
||||
<FormSelectInput Id="instance-agent" Label="Agent" @bind-Value="form.SelectedAgentGuid">
|
||||
<option value="" selected>Select which agent will run the instance...</option>
|
||||
@foreach (var agent in allAgentsByGuid.Values.Where(static agent => agent.IsOnline).OrderBy(static agent => agent.Name)) {
|
||||
@foreach (var agent in form.AgentsByGuid.Values.Where(static agent => agent.IsOnline).OrderBy(static agent => agent.Name)) {
|
||||
@GetAgentOption(agent)
|
||||
}
|
||||
</FormSelectInput>
|
||||
}
|
||||
else {
|
||||
<FormSelectInput Id="instance-agent" Label="Agent" @bind-Value="form.SelectedAgentGuid" disabled="true">
|
||||
@if (form.SelectedAgentGuid is {} guid && allAgentsByGuid.TryGetValue(guid, out var agent)) {
|
||||
@if (form.SelectedAgentGuid is {} guid && form.AgentsByGuid.TryGetValue(guid, out var agent)) {
|
||||
@GetAgentOption(agent)
|
||||
}
|
||||
</FormSelectInput>
|
||||
@ -164,25 +160,23 @@
|
||||
@code {
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public InstanceConfiguration? EditedInstanceConfiguration { get; init; }
|
||||
public InstanceConfiguration? EditedInstanceConfiguration { get; set; }
|
||||
|
||||
private ConfigureInstanceFormModel form = null!;
|
||||
|
||||
private ImmutableDictionary<Guid, AgentWithStats> allAgentsByGuid = ImmutableDictionary<Guid, AgentWithStats>.Empty;
|
||||
private ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>> allAgentJavaRuntimes = ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>.Empty;
|
||||
|
||||
private MinecraftVersionType minecraftVersionType = MinecraftVersionType.Release;
|
||||
private ImmutableArray<MinecraftVersion> allMinecraftVersions = ImmutableArray<MinecraftVersion>.Empty;
|
||||
private ImmutableArray<MinecraftVersion> availableMinecraftVersions = ImmutableArray<MinecraftVersion>.Empty;
|
||||
|
||||
private bool IsSubmittable => form.SelectedAgentGuid != null && !form.EditContext.GetValidationMessages(form.EditContext.Field(nameof(ConfigureInstanceFormModel.SelectedAgentGuid))).Any();
|
||||
|
||||
private sealed class ConfigureInstanceFormModel : FormModel {
|
||||
private readonly InstanceAddOrEditForm page;
|
||||
public ImmutableDictionary<Guid, Agent> AgentsByGuid { get; }
|
||||
private readonly ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>> javaRuntimesByAgentGuid;
|
||||
private readonly RamAllocationUnits? editedInstanceRamAllocation;
|
||||
|
||||
public ConfigureInstanceFormModel(InstanceAddOrEditForm page, RamAllocationUnits? editedInstanceRamAllocation) {
|
||||
this.page = page;
|
||||
public ConfigureInstanceFormModel(AgentManager agentManager, AgentJavaRuntimesManager agentJavaRuntimesManager, RamAllocationUnits? editedInstanceRamAllocation) {
|
||||
this.AgentsByGuid = agentManager.GetAgents().ToImmutableDictionary();
|
||||
this.javaRuntimesByAgentGuid = agentJavaRuntimesManager.All;
|
||||
this.editedInstanceRamAllocation = editedInstanceRamAllocation;
|
||||
}
|
||||
|
||||
@ -196,14 +190,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetAgent(Guid? agentGuid, [NotNullWhen(true)] out AgentWithStats? agent) {
|
||||
return TryGet(page.allAgentsByGuid, agentGuid, out agent);
|
||||
private bool TryGetAgent(Guid? agentGuid, [NotNullWhen(true)] out Agent? agent) {
|
||||
return TryGet(AgentsByGuid, agentGuid, out agent);
|
||||
}
|
||||
|
||||
public AgentWithStats? SelectedAgent => TryGetAgent(SelectedAgentGuid, out var agent) ? agent : null;
|
||||
|
||||
public ImmutableArray<TaggedJavaRuntime> JavaRuntimesForSelectedAgent => TryGet(page.allAgentJavaRuntimes, SelectedAgentGuid, out var javaRuntimes) ? javaRuntimes : ImmutableArray<TaggedJavaRuntime>.Empty;
|
||||
|
||||
public Agent? SelectedAgent => TryGetAgent(SelectedAgentGuid, out var agent) ? agent : null;
|
||||
|
||||
public ImmutableArray<TaggedJavaRuntime> JavaRuntimesForSelectedAgent => TryGet(javaRuntimesByAgentGuid, SelectedAgentGuid, out var javaRuntimes) ? javaRuntimes : ImmutableArray<TaggedJavaRuntime>.Empty;
|
||||
|
||||
public ushort MaximumMemoryUnits => SelectedAgent?.MaxMemory.RawValue ?? 0;
|
||||
public ushort AvailableMemoryUnits => Math.Min((SelectedAgent?.AvailableMemory + editedInstanceRamAllocation)?.RawValue ?? MaximumMemoryUnits, MaximumMemoryUnits);
|
||||
private ushort selectedMemoryUnits = 4;
|
||||
@ -269,17 +263,8 @@
|
||||
}
|
||||
|
||||
protected override void OnInitialized() {
|
||||
form = new ConfigureInstanceFormModel(this, EditedInstanceConfiguration?.MemoryAllocation);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
var agentJavaRuntimesTask = ControllerConnection.Send<GetAgentJavaRuntimes, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(new GetAgentJavaRuntimes(), TimeSpan.FromSeconds(30));
|
||||
var minecraftVersionsTask = ControllerConnection.Send<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(new GetMinecraftVersionsMessage(), TimeSpan.FromSeconds(30));
|
||||
form = new ConfigureInstanceFormModel(AgentManager, AgentJavaRuntimesManager, EditedInstanceConfiguration?.MemoryAllocation);
|
||||
|
||||
allAgentsByGuid = AgentManager.ToDictionaryByGuid();
|
||||
allAgentJavaRuntimes = await agentJavaRuntimesTask;
|
||||
allMinecraftVersions = await minecraftVersionsTask;
|
||||
|
||||
if (EditedInstanceConfiguration != null) {
|
||||
form.SelectedAgentGuid = EditedInstanceConfiguration.AgentGuid;
|
||||
form.InstanceName = EditedInstanceConfiguration.InstanceName;
|
||||
@ -290,21 +275,28 @@
|
||||
form.MemoryUnits = EditedInstanceConfiguration.MemoryAllocation.RawValue;
|
||||
form.JavaRuntimeGuid = EditedInstanceConfiguration.JavaRuntimeGuid;
|
||||
form.JvmArguments = JvmArgumentsHelper.Join(EditedInstanceConfiguration.JvmArguments);
|
||||
|
||||
minecraftVersionType = allMinecraftVersions.FirstOrDefault(version => version.Id == EditedInstanceConfiguration.MinecraftVersion)?.Type ?? minecraftVersionType;
|
||||
}
|
||||
|
||||
|
||||
form.EditContext.RevalidateWhenFieldChanges(tracked: nameof(ConfigureInstanceFormModel.SelectedAgentGuid), revalidated: nameof(ConfigureInstanceFormModel.MemoryUnits));
|
||||
form.EditContext.RevalidateWhenFieldChanges(tracked: nameof(ConfigureInstanceFormModel.SelectedAgentGuid), revalidated: nameof(ConfigureInstanceFormModel.JavaRuntimeGuid));
|
||||
form.EditContext.RevalidateWhenFieldChanges(tracked: nameof(ConfigureInstanceFormModel.SelectedAgentGuid), revalidated: nameof(ConfigureInstanceFormModel.ServerPort));
|
||||
form.EditContext.RevalidateWhenFieldChanges(tracked: nameof(ConfigureInstanceFormModel.SelectedAgentGuid), revalidated: nameof(ConfigureInstanceFormModel.RconPort));
|
||||
form.EditContext.RevalidateWhenFieldChanges(tracked: nameof(ConfigureInstanceFormModel.ServerPort), revalidated: nameof(ConfigureInstanceFormModel.RconPort));
|
||||
|
||||
SetMinecraftVersionType(minecraftVersionType);
|
||||
}
|
||||
|
||||
private void SetMinecraftVersionType(MinecraftVersionType type) {
|
||||
protected override async Task OnInitializedAsync() {
|
||||
if (EditedInstanceConfiguration != null) {
|
||||
var allMinecraftVersions = await MinecraftVersions.GetVersions(CancellationToken.None);
|
||||
minecraftVersionType = allMinecraftVersions.FirstOrDefault(version => version.Id == EditedInstanceConfiguration.MinecraftVersion)?.Type ?? minecraftVersionType;
|
||||
}
|
||||
|
||||
await SetMinecraftVersionType(minecraftVersionType);
|
||||
}
|
||||
|
||||
private async Task SetMinecraftVersionType(MinecraftVersionType type) {
|
||||
minecraftVersionType = type;
|
||||
|
||||
var allMinecraftVersions = await MinecraftVersions.GetVersions(CancellationToken.None);
|
||||
availableMinecraftVersions = allMinecraftVersions.Where(version => version.Type == type).ToImmutableArray();
|
||||
|
||||
if (!availableMinecraftVersions.IsEmpty && !availableMinecraftVersions.Any(version => version.Id == form.MinecraftVersion)) {
|
||||
@ -313,11 +305,6 @@
|
||||
}
|
||||
|
||||
private async Task AddOrEditInstance(EditContext context) {
|
||||
var loggedInUserGuid = await GetUserGuid();
|
||||
if (loggedInUserGuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedAgent = form.SelectedAgent;
|
||||
if (selectedAgent == null) {
|
||||
return;
|
||||
@ -338,13 +325,14 @@
|
||||
JvmArgumentsHelper.Split(form.JvmArguments)
|
||||
);
|
||||
|
||||
var result = await InstanceManager.CreateOrUpdateInstance(loggedInUserGuid.Value, instance);
|
||||
if (result.Is(CreateOrUpdateInstanceResult.Success)) {
|
||||
await Nav.NavigateTo("instances/" + instance.InstanceGuid);
|
||||
var result = await InstanceManager.AddOrEditInstance(instance);
|
||||
if (result.Is(AddOrEditInstanceResult.Success)) {
|
||||
await (EditedInstanceConfiguration == null ? AuditLog.AddInstanceCreatedEvent(instance.InstanceGuid) : AuditLog.AddInstanceEditedEvent(instance.InstanceGuid));
|
||||
Nav.NavigateTo("instances/" + instance.InstanceGuid);
|
||||
}
|
||||
else {
|
||||
form.SubmitModel.StopSubmitting(result.ToSentence(CreateOrUpdateInstanceResultExtensions.ToSentence));
|
||||
form.SubmitModel.StopSubmitting(result.ToSentence(AddOrEditInstanceResultExtensions.ToSentence));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user