1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2026-01-12 17:58:15 +01:00
Files

112 lines
4.5 KiB
C#

using System.Collections.Concurrent;
using System.Collections.Immutable;
using Akka.Actor;
using Phantom.Common.Data;
using Phantom.Common.Data.Replies;
using Phantom.Common.Data.Web.Agent;
using Phantom.Common.Data.Web.Users;
using Phantom.Common.Messages.Agent.Handshake;
using Phantom.Common.Messages.Agent.ToAgent;
using Phantom.Controller.Database;
using Phantom.Controller.Minecraft;
using Phantom.Controller.Services.Users.Sessions;
using Phantom.Utils.Actor;
using Phantom.Utils.Logging;
using Phantom.Utils.Rpc;
using Serilog;
namespace Phantom.Controller.Services.Agents;
sealed class AgentManager(
IActorRefFactory actorSystem,
AgentConnectionKeys agentConnectionKeys,
ControllerState controllerState,
MinecraftVersions minecraftVersions,
IDbContextProvider dbProvider,
CancellationToken cancellationToken
) {
private static readonly ILogger Logger = PhantomLogger.Create<AgentManager>();
private readonly ConcurrentDictionary<Guid, ActorRef<AgentActor.ICommand>> agentsByAgentGuid = new ();
public async Task Initialize() {
await using var ctx = dbProvider.Eager();
await foreach (var entity in ctx.Agents.AsAsyncEnumerable().WithCancellation(cancellationToken)) {
var agentGuid = entity.AgentGuid;
if (AddAgent(loggedInUserGuid: null, agentGuid, entity.Configuration, entity.AuthSecret, entity.RuntimeInfo)) {
Logger.Information("Loaded agent \"{AgentName}\" (GUID {AgentGuid}) from database.", entity.Name, agentGuid);
}
}
}
private bool AddAgent(Guid? loggedInUserGuid, Guid agentGuid, AgentConfiguration configuration, AuthSecret authSecret, AgentRuntimeInfo runtimeInfo) {
var init = new AgentActor.Init(loggedInUserGuid, agentGuid, configuration, authSecret, runtimeInfo, agentConnectionKeys, controllerState, minecraftVersions, dbProvider, cancellationToken);
var name = "Agent:" + agentGuid;
return agentsByAgentGuid.TryAdd(agentGuid, actorSystem.ActorOf(AgentActor.Factory(init), name));
}
public async Task<ImmutableArray<ConfigureInstanceMessage>?> RegisterAgent(Guid agentGuid, AgentRegistration registration) {
if (!agentsByAgentGuid.TryGetValue(agentGuid, out var agentActor)) {
return null;
}
var runtimeInfo = AgentRuntimeInfo.From(registration.AgentInfo);
return await agentActor.Request(new AgentActor.RegisterCommand(runtimeInfo, registration.JavaRuntimes), cancellationToken);
}
public async Task<AuthSecret?> GetAgentAuthSecret(Guid agentGuid) {
if (agentsByAgentGuid.TryGetValue(agentGuid, out var agent)) {
return await agent.Request(new AgentActor.GetAuthSecretCommand(), cancellationToken);
}
else {
return null;
}
}
public bool TellAgent(Guid agentGuid, AgentActor.ICommand command) {
if (agentsByAgentGuid.TryGetValue(agentGuid, out var agent)) {
agent.Tell(command);
return true;
}
else {
Logger.Warning("Could not deliver command {CommandType} to unknown agent {AgentGuid}.", command.GetType().Name, agentGuid);
return false;
}
}
public Result<CreateOrUpdateAgentResult, UserActionFailure> CreateOrUpdateAgent(LoggedInUser loggedInUser, Guid agentGuid, AgentConfiguration configuration) {
if (!loggedInUser.CheckPermission(Permission.ManageAllAgents)) {
return UserActionFailure.NotAuthorized;
}
if (configuration.AgentName.Length == 0) {
return CreateOrUpdateAgentResult.AgentNameMustNotBeEmpty;
}
if (agentsByAgentGuid.TryGetValue(agentGuid, out var agent)) {
agent.Tell(new AgentActor.ConfigureAgentCommand(loggedInUser.Guid!.Value, configuration));
}
else {
AddAgent(loggedInUser.Guid!.Value, agentGuid, configuration, AuthSecret.Generate(), new AgentRuntimeInfo());
}
return CreateOrUpdateAgentResult.Success;
}
public async Task<Result<TReply, UserInstanceActionFailure>> DoInstanceAction<TCommand, TReply>(LoggedInUser loggedInUser, Permission requiredPermission, Guid agentGuid, Func<Guid, TCommand> commandFactoryFromLoggedInUserGuid) where TCommand : class, AgentActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
if (!loggedInUser.HasAccessToAgent(agentGuid) || !loggedInUser.CheckPermission(requiredPermission)) {
return (UserInstanceActionFailure) UserActionFailure.NotAuthorized;
}
if (!agentsByAgentGuid.TryGetValue(agentGuid, out var agent)) {
return (UserInstanceActionFailure) InstanceActionFailure.AgentDoesNotExist;
}
var command = commandFactoryFromLoggedInUserGuid(loggedInUser.Guid!.Value);
var result = await agent.Request(command, cancellationToken);
return result.MapError(static error => (UserInstanceActionFailure) error);
}
}