mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-11-25 16:42:54 +01:00
Compare commits
5 Commits
a89a8738f9
...
4ac60f61eb
Author | SHA1 | Date | |
---|---|---|---|
4ac60f61eb | |||
489c68ad8e | |||
5b74038c9d | |||
24d41dc9f8 | |||
627e7436fd |
@ -9,10 +9,10 @@
|
|||||||
<env name="AGENT_NAME" value="Agent 1" />
|
<env name="AGENT_NAME" value="Agent 1" />
|
||||||
<env name="ALLOWED_RCON_PORTS" value="25575,27000,27001" />
|
<env name="ALLOWED_RCON_PORTS" value="25575,27000,27001" />
|
||||||
<env name="ALLOWED_SERVER_PORTS" value="25565,26000,26001" />
|
<env name="ALLOWED_SERVER_PORTS" value="25565,26000,26001" />
|
||||||
|
<env name="CONTROLLER_HOST" value="localhost" />
|
||||||
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
||||||
<env name="MAX_INSTANCES" value="3" />
|
<env name="MAX_INSTANCES" value="3" />
|
||||||
<env name="MAX_MEMORY" value="12G" />
|
<env name="MAX_MEMORY" value="12G" />
|
||||||
<env name="SERVER_HOST" value="localhost" />
|
|
||||||
</envs>
|
</envs>
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
<env name="AGENT_NAME" value="Agent 2" />
|
<env name="AGENT_NAME" value="Agent 2" />
|
||||||
<env name="ALLOWED_RCON_PORTS" value="27002-27006" />
|
<env name="ALLOWED_RCON_PORTS" value="27002-27006" />
|
||||||
<env name="ALLOWED_SERVER_PORTS" value="26002-26006" />
|
<env name="ALLOWED_SERVER_PORTS" value="26002-26006" />
|
||||||
|
<env name="CONTROLLER_HOST" value="localhost" />
|
||||||
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
||||||
<env name="MAX_INSTANCES" value="5" />
|
<env name="MAX_INSTANCES" value="5" />
|
||||||
<env name="MAX_MEMORY" value="10G" />
|
<env name="MAX_MEMORY" value="10G" />
|
||||||
<env name="SERVER_HOST" value="localhost" />
|
|
||||||
</envs>
|
</envs>
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
<env name="AGENT_NAME" value="Agent 3" />
|
<env name="AGENT_NAME" value="Agent 3" />
|
||||||
<env name="ALLOWED_RCON_PORTS" value="27007" />
|
<env name="ALLOWED_RCON_PORTS" value="27007" />
|
||||||
<env name="ALLOWED_SERVER_PORTS" value="26007" />
|
<env name="ALLOWED_SERVER_PORTS" value="26007" />
|
||||||
|
<env name="CONTROLLER_HOST" value="localhost" />
|
||||||
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
<env name="JAVA_SEARCH_PATH" value="~/.jdks" />
|
||||||
<env name="MAX_INSTANCES" value="1" />
|
<env name="MAX_INSTANCES" value="1" />
|
||||||
<env name="MAX_MEMORY" value="2560M" />
|
<env name="MAX_MEMORY" value="2560M" />
|
||||||
<env name="SERVER_HOST" value="localhost" />
|
|
||||||
</envs>
|
</envs>
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Server + Agent x3" type="CompoundRunConfigurationType">
|
<configuration default="false" name="Controller + Agent x3" type="CompoundRunConfigurationType">
|
||||||
<toRun name="Agent 1" type="DotNetProject" />
|
<toRun name="Agent 1" type="DotNetProject" />
|
||||||
<toRun name="Agent 2" type="DotNetProject" />
|
<toRun name="Agent 2" type="DotNetProject" />
|
||||||
<toRun name="Agent 3" type="DotNetProject" />
|
<toRun name="Agent 3" type="DotNetProject" />
|
||||||
<toRun name="Server" type="DotNetProject" />
|
<toRun name="Controller" type="DotNetProject" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
7
.run/Controller + Agent.run.xml
Normal file
7
.run/Controller + Agent.run.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Controller + Agent" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="Agent 1" type="DotNetProject" />
|
||||||
|
<toRun name="Controller" type="DotNetProject" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -1,18 +1,17 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Server" type="DotNetProject" factoryName=".NET Project">
|
<configuration default="false" name="Controller" type="DotNetProject" factoryName=".NET Project">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Controller/debug/Phantom.Controller.exe" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Controller/debug/Phantom.Controller.exe" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.workdir/Server" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.workdir/Controller" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<envs>
|
<envs>
|
||||||
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
|
<env name="AGENT_RPC_SERVER_HOST" value="localhost" />
|
||||||
<env name="PG_DATABASE" value="postgres" />
|
<env name="PG_DATABASE" value="postgres" />
|
||||||
<env name="PG_HOST" value="localhost" />
|
<env name="PG_HOST" value="localhost" />
|
||||||
<env name="PG_PASS" value="development" />
|
<env name="PG_PASS" value="development" />
|
||||||
<env name="PG_PORT" value="9402" />
|
<env name="PG_PORT" value="9403" />
|
||||||
<env name="PG_USER" value="postgres" />
|
<env name="PG_USER" value="postgres" />
|
||||||
<env name="RPC_SERVER_HOST" value="localhost" />
|
<env name="WEB_RPC_SERVER_HOST" value="localhost" />
|
||||||
<env name="WEB_SERVER_HOST" value="localhost" />
|
|
||||||
</envs>
|
</envs>
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
<option name="USE_MONO" value="0" />
|
<option name="USE_MONO" value="0" />
|
@ -1,7 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Server + Agent" type="CompoundRunConfigurationType">
|
|
||||||
<toRun name="Agent 1" type="DotNetProject" />
|
|
||||||
<toRun name="Server" type="DotNetProject" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
24
.run/Web.run.xml
Normal file
24
.run/Web.run.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Web" type="DotNetProject" factoryName=".NET Project">
|
||||||
|
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Web/debug/Phantom.Web.exe" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.workdir/Web" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
|
<envs>
|
||||||
|
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
|
||||||
|
<env name="WEB_SERVER_HOST" value="localhost" />
|
||||||
|
</envs>
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Web/Phantom.Web/Phantom.Web.csproj" />
|
||||||
|
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
|
||||||
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
|
<option name="PROJECT_TFM" value="net8.0" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
1
.workdir/Controller/.gitignore
vendored
Normal file
1
.workdir/Controller/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
2
.workdir/Controller/secrets/web.key
Normal file
2
.workdir/Controller/secrets/web.key
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
±™h?־<>ֹBx
|
||||||
|
<02>
–f-<2D>¢יא<01>“ש"8”כיJ–<4A>Jn/וda
|
1
.workdir/Controller/secrets/web.secret
Normal file
1
.workdir/Controller/secrets/web.secret
Normal file
@ -0,0 +1 @@
|
|||||||
|
TΦ./gϋΏNρ°t<C2B0>$Ν!Β(ƒρ#η~ΖΞ}<14><:
|
@ -1,5 +1,7 @@
|
|||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Rpc;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Rpc;
|
namespace Phantom.Agent.Rpc;
|
||||||
@ -9,10 +11,10 @@ sealed class KeepAliveLoop {
|
|||||||
|
|
||||||
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromSeconds(10);
|
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
private readonly RpcServerConnection connection;
|
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
||||||
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
||||||
|
|
||||||
public KeepAliveLoop(RpcServerConnection connection) {
|
public KeepAliveLoop(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
Task.Run(Run);
|
Task.Run(Run);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Messages\Phantom.Common.Messages.csproj" />
|
<ProjectReference Include="..\..\Common\Phantom.Common.Messages.Agent\Phantom.Common.Messages.Agent.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
43
Agent/Phantom.Agent.Rpc/RpcClientRuntime.cs
Normal file
43
Agent/Phantom.Agent.Rpc/RpcClientRuntime.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using NetMQ;
|
||||||
|
using NetMQ.Sockets;
|
||||||
|
using Phantom.Common.Data.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Rpc;
|
||||||
|
using Phantom.Utils.Tasks;
|
||||||
|
|
||||||
|
namespace Phantom.Agent.Rpc;
|
||||||
|
|
||||||
|
public sealed class RpcClientRuntime : RpcClientRuntime<IMessageToAgentListener, IMessageToControllerListener, RegisterAgentMessage> {
|
||||||
|
public static Task Launch(RpcConfiguration config, AuthToken authToken, AgentInfo agentInfo, Func<RpcConnectionToServer<IMessageToControllerListener>, IMessageToAgentListener> listenerFactory, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) {
|
||||||
|
return new RpcClientRuntime(config, agentInfo.Guid, listenerFactory, new RegisterAgentMessage(authToken, agentInfo), disconnectSemaphore, receiveCancellationToken).Launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly RpcConfiguration config;
|
||||||
|
private readonly Guid agentGuid;
|
||||||
|
|
||||||
|
private RpcClientRuntime(RpcConfiguration config, Guid agentGuid, Func<RpcConnectionToServer<IMessageToControllerListener>, IMessageToAgentListener> messageListenerFactory, RegisterAgentMessage registerAgentMessage, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(config, AgentMessageRegistries.Definitions, messageListenerFactory, registerAgentMessage, disconnectSemaphore, receiveCancellationToken) {
|
||||||
|
this.config = config;
|
||||||
|
this.agentGuid = agentGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RunWithConnection(ClientSocket socket, RpcConnectionToServer<IMessageToControllerListener> connection, TaskManager taskManager) {
|
||||||
|
ServerMessaging.SetCurrentConnection(connection);
|
||||||
|
|
||||||
|
var keepAliveLoop = new KeepAliveLoop(connection);
|
||||||
|
try {
|
||||||
|
base.RunWithConnection(socket, connection, taskManager);
|
||||||
|
} finally {
|
||||||
|
keepAliveLoop.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Disconnect(ClientSocket socket) {
|
||||||
|
var unregisterMessageBytes = AgentMessageRegistries.ToController.Write(new UnregisterAgentMessage(agentGuid)).ToArray();
|
||||||
|
try {
|
||||||
|
await socket.SendAsync(unregisterMessageBytes).AsTask().WaitAsync(TimeSpan.FromSeconds(5), CancellationToken.None);
|
||||||
|
} catch (TimeoutException) {
|
||||||
|
config.RuntimeLogger.Error("Timed out communicating agent shutdown with the controller.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,107 +0,0 @@
|
|||||||
using NetMQ;
|
|
||||||
using NetMQ.Sockets;
|
|
||||||
using Phantom.Common.Data.Agent;
|
|
||||||
using Phantom.Common.Messages;
|
|
||||||
using Phantom.Common.Messages.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.ToServer;
|
|
||||||
using Phantom.Utils.Rpc;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
using Phantom.Utils.Tasks;
|
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
namespace Phantom.Agent.Rpc;
|
|
||||||
|
|
||||||
public sealed class RpcLauncher : RpcRuntime<ClientSocket> {
|
|
||||||
public static Task Launch(RpcConfiguration config, AgentAuthToken authToken, AgentInfo agentInfo, Func<RpcServerConnection, IMessageToAgentListener> listenerFactory, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) {
|
|
||||||
var socket = new ClientSocket();
|
|
||||||
var options = socket.Options;
|
|
||||||
|
|
||||||
options.CurveServerCertificate = config.ServerCertificate;
|
|
||||||
options.CurveCertificate = new NetMQCertificate();
|
|
||||||
options.HelloMessage = MessageRegistries.ToServer.Write(new RegisterAgentMessage(authToken, agentInfo)).ToArray();
|
|
||||||
|
|
||||||
return new RpcLauncher(config, socket, agentInfo.Guid, listenerFactory, disconnectSemaphore, receiveCancellationToken).Launch();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly RpcConfiguration config;
|
|
||||||
private readonly Guid agentGuid;
|
|
||||||
private readonly Func<RpcServerConnection, IMessageToAgentListener> messageListenerFactory;
|
|
||||||
|
|
||||||
private readonly SemaphoreSlim disconnectSemaphore;
|
|
||||||
private readonly CancellationToken receiveCancellationToken;
|
|
||||||
|
|
||||||
private RpcLauncher(RpcConfiguration config, ClientSocket socket, Guid agentGuid, Func<RpcServerConnection, IMessageToAgentListener> messageListenerFactory, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(config, socket) {
|
|
||||||
this.config = config;
|
|
||||||
this.agentGuid = agentGuid;
|
|
||||||
this.messageListenerFactory = messageListenerFactory;
|
|
||||||
this.disconnectSemaphore = disconnectSemaphore;
|
|
||||||
this.receiveCancellationToken = receiveCancellationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Connect(ClientSocket socket) {
|
|
||||||
var logger = config.RuntimeLogger;
|
|
||||||
var url = config.TcpUrl;
|
|
||||||
|
|
||||||
logger.Information("Starting ZeroMQ client and connecting to {Url}...", url);
|
|
||||||
socket.Connect(url);
|
|
||||||
logger.Information("ZeroMQ client ready.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Run(ClientSocket socket, MessageReplyTracker replyTracker, TaskManager taskManager) {
|
|
||||||
var connection = new RpcServerConnection(socket, replyTracker);
|
|
||||||
ServerMessaging.SetCurrentConnection(connection);
|
|
||||||
|
|
||||||
var logger = config.RuntimeLogger;
|
|
||||||
var handler = new MessageToAgentHandler(messageListenerFactory(connection), logger, taskManager, receiveCancellationToken);
|
|
||||||
var keepAliveLoop = new KeepAliveLoop(connection);
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (!receiveCancellationToken.IsCancellationRequested) {
|
|
||||||
var data = socket.Receive(receiveCancellationToken);
|
|
||||||
|
|
||||||
LogMessageType(logger, data);
|
|
||||||
|
|
||||||
if (data.Length > 0) {
|
|
||||||
MessageRegistries.ToAgent.Handle(data, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (OperationCanceledException) {
|
|
||||||
// Ignore.
|
|
||||||
} finally {
|
|
||||||
logger.Debug("ZeroMQ client stopped receiving messages.");
|
|
||||||
|
|
||||||
disconnectSemaphore.Wait(CancellationToken.None);
|
|
||||||
keepAliveLoop.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LogMessageType(ILogger logger, ReadOnlyMemory<byte> data) {
|
|
||||||
if (!logger.IsEnabled(LogEventLevel.Verbose)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.Length > 0 && MessageRegistries.ToAgent.TryGetType(data, out var type)) {
|
|
||||||
logger.Verbose("Received {MessageType} ({Bytes} B) from server.", type.Name, data.Length);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.Verbose("Received {Bytes} B message from server.", data.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task Disconnect() {
|
|
||||||
var unregisterTimeoutTask = Task.Delay(TimeSpan.FromSeconds(5), CancellationToken.None);
|
|
||||||
var finishedTask = await Task.WhenAny(ServerMessaging.Send(new UnregisterAgentMessage(agentGuid)), unregisterTimeoutTask);
|
|
||||||
if (finishedTask == unregisterTimeoutTask) {
|
|
||||||
config.RuntimeLogger.Error("Timed out communicating agent shutdown with the server.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class MessageToAgentHandler : MessageHandler<IMessageToAgentListener> {
|
|
||||||
public MessageToAgentHandler(IMessageToAgentListener listener, ILogger logger, TaskManager taskManager, CancellationToken cancellationToken) : base(listener, logger, taskManager, cancellationToken) {}
|
|
||||||
|
|
||||||
protected override Task SendReply(uint sequenceId, byte[] serializedReply) {
|
|
||||||
return ServerMessaging.Send(new ReplyMessage(sequenceId, serializedReply));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
using NetMQ;
|
|
||||||
using NetMQ.Sockets;
|
|
||||||
using Phantom.Common.Messages;
|
|
||||||
using Phantom.Common.Messages.BiDirectional;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Agent.Rpc;
|
|
||||||
|
|
||||||
public sealed class RpcServerConnection {
|
|
||||||
private readonly ClientSocket socket;
|
|
||||||
private readonly MessageReplyTracker replyTracker;
|
|
||||||
|
|
||||||
internal RpcServerConnection(ClientSocket socket, MessageReplyTracker replyTracker) {
|
|
||||||
this.socket = socket;
|
|
||||||
this.replyTracker = replyTracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task Send<TMessage>(TMessage message) where TMessage : IMessageToServer {
|
|
||||||
var bytes = MessageRegistries.ToServer.Write(message).ToArray();
|
|
||||||
if (bytes.Length > 0) {
|
|
||||||
await socket.SendAsync(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<TReply?> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToServer<TReply> where TReply : class {
|
|
||||||
var sequenceId = replyTracker.RegisterReply();
|
|
||||||
|
|
||||||
var bytes = MessageRegistries.ToServer.Write<TMessage, TReply>(sequenceId, message).ToArray();
|
|
||||||
if (bytes.Length == 0) {
|
|
||||||
replyTracker.ForgetReply(sequenceId);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await socket.SendAsync(bytes);
|
|
||||||
return await replyTracker.WaitForReply<TReply>(sequenceId, waitForReplyTime, waitForReplyCancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(ReplyMessage message) {
|
|
||||||
replyTracker.ReceiveReply(message.SequenceId, message.SerializedReply);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages;
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Utils.Rpc;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Rpc;
|
namespace Phantom.Agent.Rpc;
|
||||||
@ -7,12 +8,12 @@ namespace Phantom.Agent.Rpc;
|
|||||||
public static class ServerMessaging {
|
public static class ServerMessaging {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create(nameof(ServerMessaging));
|
private static readonly ILogger Logger = PhantomLogger.Create(nameof(ServerMessaging));
|
||||||
|
|
||||||
private static RpcServerConnection? CurrentConnection { get; set; }
|
private static RpcConnectionToServer<IMessageToControllerListener>? CurrentConnection { get; set; }
|
||||||
private static RpcServerConnection CurrentConnectionOrThrow => CurrentConnection ?? throw new InvalidOperationException("Server connection not ready.");
|
private static RpcConnectionToServer<IMessageToControllerListener> CurrentConnectionOrThrow => CurrentConnection ?? throw new InvalidOperationException("Server connection not ready.");
|
||||||
|
|
||||||
private static readonly object SetCurrentConnectionLock = new ();
|
private static readonly object SetCurrentConnectionLock = new ();
|
||||||
|
|
||||||
internal static void SetCurrentConnection(RpcServerConnection connection) {
|
internal static void SetCurrentConnection(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
||||||
lock (SetCurrentConnectionLock) {
|
lock (SetCurrentConnectionLock) {
|
||||||
if (CurrentConnection != null) {
|
if (CurrentConnection != null) {
|
||||||
throw new InvalidOperationException("Server connection can only be set once.");
|
throw new InvalidOperationException("Server connection can only be set once.");
|
||||||
@ -24,11 +25,11 @@ public static class ServerMessaging {
|
|||||||
Logger.Information("Server connection ready.");
|
Logger.Information("Server connection ready.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task Send<TMessage>(TMessage message) where TMessage : IMessageToServer {
|
public static Task Send<TMessage>(TMessage message) where TMessage : IMessageToController {
|
||||||
return CurrentConnectionOrThrow.Send(message);
|
return CurrentConnectionOrThrow.Send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<TReply?> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToServer<TReply> where TReply : class {
|
public static Task<TReply?> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToController<TReply> where TReply : class {
|
||||||
return CurrentConnectionOrThrow.Send<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken);
|
return CurrentConnectionOrThrow.Send<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ using Phantom.Common.Data.Instance;
|
|||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Services.Instances;
|
namespace Phantom.Agent.Services.Instances;
|
||||||
|
@ -2,7 +2,7 @@ using System.Collections.Immutable;
|
|||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using Phantom.Agent.Rpc;
|
using Phantom.Agent.Rpc;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
using Phantom.Utils.Tasks;
|
using Phantom.Utils.Tasks;
|
||||||
|
|
||||||
namespace Phantom.Agent.Services.Instances;
|
namespace Phantom.Agent.Services.Instances;
|
||||||
|
@ -14,7 +14,7 @@ using Phantom.Common.Data.Instance;
|
|||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
using Phantom.Utils.IO;
|
using Phantom.Utils.IO;
|
||||||
using Phantom.Utils.Tasks;
|
using Phantom.Utils.Tasks;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Messages\Phantom.Common.Messages.csproj" />
|
<ProjectReference Include="..\..\Common\Phantom.Common.Messages.Agent\Phantom.Common.Messages.Agent.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Agent.Minecraft\Phantom.Agent.Minecraft.csproj" />
|
<ProjectReference Include="..\Phantom.Agent.Minecraft\Phantom.Agent.Minecraft.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Agent.Rpc\Phantom.Agent.Rpc.csproj" />
|
<ProjectReference Include="..\Phantom.Agent.Rpc\Phantom.Agent.Rpc.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Common.Messages;
|
using Phantom.Common.Messages.Agent;
|
||||||
using Phantom.Common.Messages.BiDirectional;
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
using Phantom.Common.Messages.ToAgent;
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Rpc;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
@ -14,11 +15,11 @@ namespace Phantom.Agent.Services.Rpc;
|
|||||||
public sealed class MessageListener : IMessageToAgentListener {
|
public sealed class MessageListener : IMessageToAgentListener {
|
||||||
private static ILogger Logger { get; } = PhantomLogger.Create<MessageListener>();
|
private static ILogger Logger { get; } = PhantomLogger.Create<MessageListener>();
|
||||||
|
|
||||||
private readonly RpcServerConnection connection;
|
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
||||||
private readonly AgentServices agent;
|
private readonly AgentServices agent;
|
||||||
private readonly CancellationTokenSource shutdownTokenSource;
|
private readonly CancellationTokenSource shutdownTokenSource;
|
||||||
|
|
||||||
public MessageListener(RpcServerConnection connection, AgentServices agent, CancellationTokenSource shutdownTokenSource) {
|
public MessageListener(RpcConnectionToServer<IMessageToControllerListener> connection, AgentServices agent, CancellationTokenSource shutdownTokenSource) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.agent = agent;
|
this.agent = agent;
|
||||||
this.shutdownTokenSource = shutdownTokenSource;
|
this.shutdownTokenSource = shutdownTokenSource;
|
||||||
@ -40,7 +41,7 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ServerMessaging.Send(new AdvertiseJavaRuntimesMessage(agent.JavaRuntimeRepository.All));
|
await connection.Send(new AdvertiseJavaRuntimesMessage(agent.JavaRuntimeRepository.All));
|
||||||
await agent.InstanceSessionManager.RefreshAgentStatus();
|
await agent.InstanceSessionManager.RefreshAgentStatus();
|
||||||
|
|
||||||
return NoReply.Instance;
|
return NoReply.Instance;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using NetMQ;
|
using NetMQ;
|
||||||
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
using Phantom.Utils.Cryptography;
|
using Phantom.Utils.Cryptography;
|
||||||
@ -10,7 +11,7 @@ namespace Phantom.Agent;
|
|||||||
static class AgentKey {
|
static class AgentKey {
|
||||||
private static ILogger Logger { get; } = PhantomLogger.Create(nameof(AgentKey));
|
private static ILogger Logger { get; } = PhantomLogger.Create(nameof(AgentKey));
|
||||||
|
|
||||||
public static Task<(NetMQCertificate, AgentAuthToken)?> Load(string? agentKeyToken, string? agentKeyFilePath) {
|
public static Task<(NetMQCertificate, AuthToken)?> Load(string? agentKeyToken, string? agentKeyFilePath) {
|
||||||
if (agentKeyFilePath != null) {
|
if (agentKeyFilePath != null) {
|
||||||
return LoadFromFile(agentKeyFilePath);
|
return LoadFromFile(agentKeyFilePath);
|
||||||
}
|
}
|
||||||
@ -22,7 +23,7 @@ static class AgentKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<(NetMQCertificate, AgentAuthToken)?> LoadFromFile(string agentKeyFilePath) {
|
private static async Task<(NetMQCertificate, AuthToken)?> LoadFromFile(string agentKeyFilePath) {
|
||||||
if (!File.Exists(agentKeyFilePath)) {
|
if (!File.Exists(agentKeyFilePath)) {
|
||||||
Logger.Fatal("Missing agent key file: {AgentKeyFilePath}", agentKeyFilePath);
|
Logger.Fatal("Missing agent key file: {AgentKeyFilePath}", agentKeyFilePath);
|
||||||
return null;
|
return null;
|
||||||
@ -41,7 +42,7 @@ static class AgentKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (NetMQCertificate, AgentAuthToken)? LoadFromToken(string agentKey) {
|
private static (NetMQCertificate, AuthToken)? LoadFromToken(string agentKey) {
|
||||||
try {
|
try {
|
||||||
return LoadFromBytes(TokenGenerator.DecodeBytes(agentKey));
|
return LoadFromBytes(TokenGenerator.DecodeBytes(agentKey));
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
||||||
@ -50,11 +51,11 @@ static class AgentKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (NetMQCertificate, AgentAuthToken)? LoadFromBytes(byte[] agentKey) {
|
private static (NetMQCertificate, AuthToken)? LoadFromBytes(byte[] agentKey) {
|
||||||
var (publicKey, agentToken) = AgentKeyData.FromBytes(agentKey);
|
var (publicKey, agentToken) = ConnectionCommonKey.FromBytes(agentKey);
|
||||||
var serverCertificate = NetMQCertificate.FromPublicKey(publicKey);
|
var controllerCertificate = NetMQCertificate.FromPublicKey(publicKey);
|
||||||
|
|
||||||
Logger.Information("Loaded agent key.");
|
Logger.Information("Loaded agent key.");
|
||||||
return (serverCertificate, agentToken);
|
return (controllerCertificate, agentToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using Phantom.Agent.Services;
|
|||||||
using Phantom.Agent.Services.Rpc;
|
using Phantom.Agent.Services.Rpc;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Common.Logging;
|
using Phantom.Common.Logging;
|
||||||
|
using Phantom.Common.Messages.Agent;
|
||||||
using Phantom.Utils.Rpc;
|
using Phantom.Utils.Rpc;
|
||||||
using Phantom.Utils.Runtime;
|
using Phantom.Utils.Runtime;
|
||||||
using Phantom.Utils.Tasks;
|
using Phantom.Utils.Tasks;
|
||||||
@ -26,7 +27,7 @@ try {
|
|||||||
PhantomLogger.Root.InformationHeading("Initializing Phantom Panel agent...");
|
PhantomLogger.Root.InformationHeading("Initializing Phantom Panel agent...");
|
||||||
PhantomLogger.Root.Information("Agent version: {Version}", fullVersion);
|
PhantomLogger.Root.Information("Agent version: {Version}", fullVersion);
|
||||||
|
|
||||||
var (serverHost, serverPort, javaSearchPath, agentKeyToken, agentKeyFilePath, agentName, maxInstances, maxMemory, allowedServerPorts, allowedRconPorts, maxConcurrentBackupCompressionTasks) = Variables.LoadOrStop();
|
var (controllerHost, controllerPort, javaSearchPath, agentKeyToken, agentKeyFilePath, agentName, maxInstances, maxMemory, allowedServerPorts, allowedRconPorts, maxConcurrentBackupCompressionTasks) = Variables.LoadOrStop();
|
||||||
|
|
||||||
var agentKey = await AgentKey.Load(agentKeyToken, agentKeyFilePath);
|
var agentKey = await AgentKey.Load(agentKeyToken, agentKeyFilePath);
|
||||||
if (agentKey == null) {
|
if (agentKey == null) {
|
||||||
@ -43,11 +44,11 @@ try {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (serverCertificate, agentToken) = agentKey.Value;
|
var (controllerCertificate, agentToken) = agentKey.Value;
|
||||||
var agentInfo = new AgentInfo(agentGuid.Value, agentName, ProtocolVersion, fullVersion, maxInstances, maxMemory, allowedServerPorts, allowedRconPorts);
|
var agentInfo = new AgentInfo(agentGuid.Value, agentName, ProtocolVersion, fullVersion, maxInstances, maxMemory, allowedServerPorts, allowedRconPorts);
|
||||||
var agentServices = new AgentServices(agentInfo, folders, new AgentServiceConfiguration(maxConcurrentBackupCompressionTasks));
|
var agentServices = new AgentServices(agentInfo, folders, new AgentServiceConfiguration(maxConcurrentBackupCompressionTasks));
|
||||||
|
|
||||||
MessageListener MessageListenerFactory(RpcServerConnection connection) {
|
MessageListener MessageListenerFactory(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
||||||
return new MessageListener(connection, agentServices, shutdownCancellationTokenSource);
|
return new MessageListener(connection, agentServices, shutdownCancellationTokenSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +57,8 @@ try {
|
|||||||
await agentServices.Initialize();
|
await agentServices.Initialize();
|
||||||
|
|
||||||
var rpcDisconnectSemaphore = new SemaphoreSlim(0, 1);
|
var rpcDisconnectSemaphore = new SemaphoreSlim(0, 1);
|
||||||
var rpcConfiguration = new RpcConfiguration(PhantomLogger.Create("Rpc"), PhantomLogger.Create<TaskManager>("Rpc"), serverHost, serverPort, serverCertificate);
|
var rpcConfiguration = new RpcConfiguration(PhantomLogger.Create("Rpc"), PhantomLogger.Create<TaskManager>("Rpc"), controllerHost, controllerPort, controllerCertificate);
|
||||||
var rpcTask = RpcLauncher.Launch(rpcConfiguration, agentToken, agentInfo, MessageListenerFactory, rpcDisconnectSemaphore, shutdownCancellationToken);
|
var rpcTask = RpcClientRuntime.Launch(rpcConfiguration, agentToken, agentInfo, MessageListenerFactory, rpcDisconnectSemaphore, shutdownCancellationToken);
|
||||||
try {
|
try {
|
||||||
await rpcTask.WaitAsync(shutdownCancellationToken);
|
await rpcTask.WaitAsync(shutdownCancellationToken);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -6,8 +6,8 @@ using Phantom.Utils.Runtime;
|
|||||||
namespace Phantom.Agent;
|
namespace Phantom.Agent;
|
||||||
|
|
||||||
sealed record Variables(
|
sealed record Variables(
|
||||||
string ServerHost,
|
string ControllerHost,
|
||||||
ushort ServerPort,
|
ushort ControllerPort,
|
||||||
string JavaSearchPath,
|
string JavaSearchPath,
|
||||||
string? AgentKeyToken,
|
string? AgentKeyToken,
|
||||||
string? AgentKeyFilePath,
|
string? AgentKeyFilePath,
|
||||||
@ -23,8 +23,8 @@ sealed record Variables(
|
|||||||
var javaSearchPath = EnvironmentVariables.GetString("JAVA_SEARCH_PATH").WithDefaultGetter(GetDefaultJavaSearchPath);
|
var javaSearchPath = EnvironmentVariables.GetString("JAVA_SEARCH_PATH").WithDefaultGetter(GetDefaultJavaSearchPath);
|
||||||
|
|
||||||
return new Variables(
|
return new Variables(
|
||||||
EnvironmentVariables.GetString("SERVER_HOST").Require,
|
EnvironmentVariables.GetString("CONTROLLER_HOST").Require,
|
||||||
EnvironmentVariables.GetPortNumber("SERVER_PORT").WithDefault(9401),
|
EnvironmentVariables.GetPortNumber("CONTROLLER_PORT").WithDefault(9401),
|
||||||
javaSearchPath,
|
javaSearchPath,
|
||||||
agentKeyToken,
|
agentKeyToken,
|
||||||
agentKeyFilePath,
|
agentKeyFilePath,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Controller.Database.Enums;
|
namespace Phantom.Common.Data.Web.AuditLog;
|
||||||
|
|
||||||
public enum AuditLogEventType {
|
public enum AuditLogEventType {
|
||||||
AdministratorUserCreated,
|
AdministratorUserCreated,
|
||||||
@ -6,6 +6,7 @@ public enum AuditLogEventType {
|
|||||||
UserLoggedIn,
|
UserLoggedIn,
|
||||||
UserLoggedOut,
|
UserLoggedOut,
|
||||||
UserCreated,
|
UserCreated,
|
||||||
|
UserPasswordChanged,
|
||||||
UserRolesChanged,
|
UserRolesChanged,
|
||||||
UserDeleted,
|
UserDeleted,
|
||||||
InstanceCreated,
|
InstanceCreated,
|
||||||
@ -15,13 +16,14 @@ public enum AuditLogEventType {
|
|||||||
InstanceCommandExecuted
|
InstanceCommandExecuted
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AuditLogEventTypeExtensions {
|
public static class AuditLogEventTypeExtensions {
|
||||||
private static readonly Dictionary<AuditLogEventType, AuditLogSubjectType> SubjectTypes = new () {
|
private static readonly Dictionary<AuditLogEventType, AuditLogSubjectType> SubjectTypes = new () {
|
||||||
{ AuditLogEventType.AdministratorUserCreated, AuditLogSubjectType.User },
|
{ AuditLogEventType.AdministratorUserCreated, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.AdministratorUserModified, AuditLogSubjectType.User },
|
{ AuditLogEventType.AdministratorUserModified, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.UserLoggedIn, AuditLogSubjectType.User },
|
{ AuditLogEventType.UserLoggedIn, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.UserLoggedOut, AuditLogSubjectType.User },
|
{ AuditLogEventType.UserLoggedOut, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.UserCreated, AuditLogSubjectType.User },
|
{ AuditLogEventType.UserCreated, AuditLogSubjectType.User },
|
||||||
|
{ AuditLogEventType.UserPasswordChanged, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.UserRolesChanged, AuditLogSubjectType.User },
|
{ AuditLogEventType.UserRolesChanged, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.UserDeleted, AuditLogSubjectType.User },
|
{ AuditLogEventType.UserDeleted, AuditLogSubjectType.User },
|
||||||
{ AuditLogEventType.InstanceCreated, AuditLogSubjectType.Instance },
|
{ AuditLogEventType.InstanceCreated, AuditLogSubjectType.Instance },
|
||||||
@ -39,7 +41,7 @@ static class AuditLogEventTypeExtensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static AuditLogSubjectType GetSubjectType(this AuditLogEventType type) {
|
public static AuditLogSubjectType GetSubjectType(this AuditLogEventType type) {
|
||||||
return SubjectTypes[type];
|
return SubjectTypes[type];
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phantom.Controller.Database.Enums;
|
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Audit;
|
namespace Phantom.Common.Data.Web.AuditLog;
|
||||||
|
|
||||||
public sealed record AuditLogItem(DateTime UtcTime, Guid? UserGuid, string? UserName, AuditLogEventType EventType, AuditLogSubjectType SubjectType, string? SubjectId, JsonDocument? Data);
|
public sealed record AuditLogItem(DateTime UtcTime, Guid? UserGuid, string? UserName, AuditLogEventType EventType, AuditLogSubjectType SubjectType, string? SubjectId, JsonDocument? Data);
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Controller.Database.Enums;
|
namespace Phantom.Common.Data.Web.AuditLog;
|
||||||
|
|
||||||
public enum AuditLogSubjectType {
|
public enum AuditLogSubjectType {
|
||||||
User,
|
User,
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Controller.Database.Enums;
|
namespace Phantom.Common.Data.Web.EventLog;
|
||||||
|
|
||||||
public enum EventLogEventType {
|
public enum EventLogEventType {
|
||||||
InstanceLaunchSucceded,
|
InstanceLaunchSucceded,
|
||||||
@ -10,7 +10,7 @@ public enum EventLogEventType {
|
|||||||
InstanceBackupFailed,
|
InstanceBackupFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EventLogEventTypeExtensions {
|
public static class EventLogEventTypeExtensions {
|
||||||
private static readonly Dictionary<EventLogEventType, EventLogSubjectType> SubjectTypes = new () {
|
private static readonly Dictionary<EventLogEventType, EventLogSubjectType> SubjectTypes = new () {
|
||||||
{ EventLogEventType.InstanceLaunchSucceded, EventLogSubjectType.Instance },
|
{ EventLogEventType.InstanceLaunchSucceded, EventLogSubjectType.Instance },
|
||||||
{ EventLogEventType.InstanceLaunchFailed, EventLogSubjectType.Instance },
|
{ EventLogEventType.InstanceLaunchFailed, EventLogSubjectType.Instance },
|
@ -1,6 +1,5 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phantom.Controller.Database.Enums;
|
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Events;
|
namespace Phantom.Common.Data.Web.EventLog;
|
||||||
|
|
||||||
public sealed record EventLogItem(DateTime UtcTime, Guid? AgentGuid, EventLogEventType EventType, EventLogSubjectType SubjectType, string SubjectId, JsonDocument? Data);
|
public sealed record EventLogItem(DateTime UtcTime, Guid? AgentGuid, EventLogEventType EventType, EventLogSubjectType SubjectType, string SubjectId, JsonDocument? Data);
|
@ -0,0 +1,5 @@
|
|||||||
|
namespace Phantom.Common.Data.Web.EventLog;
|
||||||
|
|
||||||
|
public enum EventLogSubjectType {
|
||||||
|
Instance
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
namespace Phantom.Controller.Minecraft;
|
namespace Phantom.Common.Data.Web.Minecraft;
|
||||||
|
|
||||||
public static class JvmArgumentsHelper {
|
public static class JvmArgumentsHelper {
|
||||||
public static ImmutableArray<string> Split(string arguments) {
|
public static ImmutableArray<string> Split(string arguments) {
|
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BCrypt.Net-Next.StrongName" />
|
||||||
|
<PackageReference Include="MemoryPack" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Controller.Services.Users;
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
public enum AddRoleError : byte {
|
public enum AddRoleError : byte {
|
||||||
NameIsEmpty,
|
NameIsEmpty,
|
28
Common/Phantom.Common.Data.Web/Users/AddUserError.cs
Normal file
28
Common/Phantom.Common.Data.Web/Users/AddUserError.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users.AddUserErrors;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users {
|
||||||
|
[MemoryPackable]
|
||||||
|
[MemoryPackUnion(0, typeof(NameIsInvalid))]
|
||||||
|
[MemoryPackUnion(1, typeof(PasswordIsInvalid))]
|
||||||
|
[MemoryPackUnion(2, typeof(NameAlreadyExists))]
|
||||||
|
[MemoryPackUnion(3, typeof(UnknownError))]
|
||||||
|
public abstract partial record AddUserError {
|
||||||
|
internal AddUserError() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users.AddUserErrors {
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record NameIsInvalid([property: MemoryPackOrder(0)] UsernameRequirementViolation Violation) : AddUserError;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record PasswordIsInvalid([property: MemoryPackOrder(0)] ImmutableArray<PasswordRequirementViolation> Violations) : AddUserError;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record NameAlreadyExists : AddUserError;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UnknownError : AddUserError;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users {
|
||||||
|
[MemoryPackable]
|
||||||
|
[MemoryPackUnion(0, typeof(Success))]
|
||||||
|
[MemoryPackUnion(1, typeof(CreationFailed))]
|
||||||
|
[MemoryPackUnion(2, typeof(UpdatingFailed))]
|
||||||
|
[MemoryPackUnion(3, typeof(AddingToRoleFailed))]
|
||||||
|
[MemoryPackUnion(4, typeof(UnknownError))]
|
||||||
|
public abstract partial record CreateOrUpdateAdministratorUserResult {
|
||||||
|
internal CreateOrUpdateAdministratorUserResult() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users.CreateOrUpdateAdministratorUserResults {
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record Success([property: MemoryPackOrder(0)] UserInfo User) : CreateOrUpdateAdministratorUserResult;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record CreationFailed([property: MemoryPackOrder(0)] AddUserError Error) : CreateOrUpdateAdministratorUserResult;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UpdatingFailed([property: MemoryPackOrder(0)] SetUserPasswordError Error) : CreateOrUpdateAdministratorUserResult;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record AddingToRoleFailed : CreateOrUpdateAdministratorUserResult;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UnknownError : CreateOrUpdateAdministratorUserResult;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Controller.Services.Users;
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
public enum DeleteUserResult : byte {
|
public enum DeleteUserResult : byte {
|
||||||
Deleted,
|
Deleted,
|
25
Common/Phantom.Common.Data.Web/Users/IdentityPermissions.cs
Normal file
25
Common/Phantom.Common.Data.Web/Users/IdentityPermissions.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
|
public sealed class IdentityPermissions {
|
||||||
|
public static IdentityPermissions None { get; } = new (ImmutableHashSet<string>.Empty);
|
||||||
|
|
||||||
|
private readonly ImmutableHashSet<string> permissionIds;
|
||||||
|
|
||||||
|
public IdentityPermissions(ImmutableHashSet<string> permissionIdsQuery) {
|
||||||
|
this.permissionIds = permissionIdsQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Check(Permission? permission) {
|
||||||
|
while (permission != null) {
|
||||||
|
if (!permissionIds.Contains(permission.Id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
permission = permission.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users.PasswordRequirementViolations;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users {
|
||||||
|
[MemoryPackable]
|
||||||
|
[MemoryPackUnion(0, typeof(TooShort))]
|
||||||
|
[MemoryPackUnion(1, typeof(MustContainLowercaseLetter))]
|
||||||
|
[MemoryPackUnion(2, typeof(MustContainUppercaseLetter))]
|
||||||
|
[MemoryPackUnion(3, typeof(MustContainDigit))]
|
||||||
|
public abstract partial record PasswordRequirementViolation {
|
||||||
|
internal PasswordRequirementViolation() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users.PasswordRequirementViolations {
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record TooShort([property: MemoryPackOrder(0)] int MinimumLength) : PasswordRequirementViolation;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record MustContainLowercaseLetter : PasswordRequirementViolation;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record MustContainUppercaseLetter : PasswordRequirementViolation;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record MustContainDigit : PasswordRequirementViolation;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Phantom.Web.Identity.Data;
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
public sealed record Permission(string Id, Permission? Parent) {
|
public sealed record Permission(string Id, Permission? Parent) {
|
||||||
private static readonly List<Permission> AllPermissions = new ();
|
private static readonly List<Permission> AllPermissions = new ();
|
9
Common/Phantom.Common.Data.Web/Users/RoleInfo.cs
Normal file
9
Common/Phantom.Common.Data.Web/Users/RoleInfo.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record RoleInfo(
|
||||||
|
[property: MemoryPackOrder(0)] Guid Guid,
|
||||||
|
[property: MemoryPackOrder(1)] string Name
|
||||||
|
);
|
24
Common/Phantom.Common.Data.Web/Users/SetUserPasswordError.cs
Normal file
24
Common/Phantom.Common.Data.Web/Users/SetUserPasswordError.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users.SetUserPasswordErrors;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users {
|
||||||
|
[MemoryPackable]
|
||||||
|
[MemoryPackUnion(0, typeof(UserNotFound))]
|
||||||
|
[MemoryPackUnion(1, typeof(PasswordIsInvalid))]
|
||||||
|
[MemoryPackUnion(2, typeof(UnknownError))]
|
||||||
|
public abstract partial record SetUserPasswordError {
|
||||||
|
internal SetUserPasswordError() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users.SetUserPasswordErrors {
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UserNotFound : SetUserPasswordError;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record PasswordIsInvalid([property: MemoryPackOrder(0)] ImmutableArray<PasswordRequirementViolation> Violations) : SetUserPasswordError;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UnknownError : SetUserPasswordError;
|
||||||
|
}
|
9
Common/Phantom.Common.Data.Web/Users/UserInfo.cs
Normal file
9
Common/Phantom.Common.Data.Web/Users/UserInfo.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record UserInfo(
|
||||||
|
[property: MemoryPackOrder(0)] Guid Guid,
|
||||||
|
[property: MemoryPackOrder(1)] string Name
|
||||||
|
);
|
12
Common/Phantom.Common.Data.Web/Users/UserPasswords.cs
Normal file
12
Common/Phantom.Common.Data.Web/Users/UserPasswords.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
|
public static class UserPasswords {
|
||||||
|
public static string Hash(string password) {
|
||||||
|
return BCrypt.Net.BCrypt.HashPassword(password, workFactor: 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Verify(string password, string hash) {
|
||||||
|
// TODO rehash
|
||||||
|
return BCrypt.Net.BCrypt.Verify(password, hash);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users.UsernameRequirementViolations;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users {
|
||||||
|
[MemoryPackable]
|
||||||
|
[MemoryPackUnion(0, typeof(IsEmpty))]
|
||||||
|
[MemoryPackUnion(1, typeof(TooLong))]
|
||||||
|
public abstract partial record UsernameRequirementViolation {
|
||||||
|
internal UsernameRequirementViolation() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Phantom.Common.Data.Web.Users.UsernameRequirementViolations {
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record IsEmpty : UsernameRequirementViolation;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record TooLong([property: MemoryPackOrder(0)] int MaxLength) : UsernameRequirementViolation;
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
namespace Phantom.Common.Data.Agent;
|
|
||||||
|
|
||||||
public static class AgentKeyData {
|
|
||||||
private const byte TokenLength = AgentAuthToken.Length;
|
|
||||||
|
|
||||||
public static byte[] ToBytes(byte[] publicKey, AgentAuthToken agentToken) {
|
|
||||||
Span<byte> agentKey = stackalloc byte[TokenLength + publicKey.Length];
|
|
||||||
agentToken.WriteTo(agentKey[..TokenLength]);
|
|
||||||
publicKey.CopyTo(agentKey[TokenLength..]);
|
|
||||||
return agentKey.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (byte[] PublicKey, AgentAuthToken AgentToken) FromBytes(byte[] agentKey) {
|
|
||||||
var token = new AgentAuthToken(agentKey[..TokenLength]);
|
|
||||||
var publicKey = agentKey[TokenLength..];
|
|
||||||
return (publicKey, token);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,18 +2,18 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
|
||||||
namespace Phantom.Common.Data.Agent;
|
namespace Phantom.Common.Data;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
||||||
public sealed partial class AgentAuthToken {
|
public sealed partial class AuthToken {
|
||||||
internal const int Length = 12;
|
internal const int Length = 12;
|
||||||
|
|
||||||
[MemoryPackOrder(0)]
|
[MemoryPackOrder(0)]
|
||||||
[MemoryPackInclude]
|
[MemoryPackInclude]
|
||||||
private readonly byte[] bytes;
|
private readonly byte[] bytes;
|
||||||
|
|
||||||
internal AgentAuthToken(byte[]? bytes) {
|
internal AuthToken(byte[]? bytes) {
|
||||||
if (bytes == null) {
|
if (bytes == null) {
|
||||||
throw new ArgumentNullException(nameof(bytes));
|
throw new ArgumentNullException(nameof(bytes));
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ public sealed partial class AgentAuthToken {
|
|||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FixedTimeEquals(AgentAuthToken providedAuthToken) {
|
public bool FixedTimeEquals(AuthToken providedAuthToken) {
|
||||||
return CryptographicOperations.FixedTimeEquals(bytes, providedAuthToken.bytes);
|
return CryptographicOperations.FixedTimeEquals(bytes, providedAuthToken.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ public sealed partial class AgentAuthToken {
|
|||||||
bytes.CopyTo(span);
|
bytes.CopyTo(span);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AgentAuthToken Generate() {
|
public static AuthToken Generate() {
|
||||||
return new AgentAuthToken(RandomNumberGenerator.GetBytes(Length));
|
return new AuthToken(RandomNumberGenerator.GetBytes(Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
18
Common/Phantom.Common.Data/ConnectionCommonKey.cs
Normal file
18
Common/Phantom.Common.Data/ConnectionCommonKey.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Phantom.Common.Data;
|
||||||
|
|
||||||
|
public readonly record struct ConnectionCommonKey(byte[] CertificatePublicKey, AuthToken AuthToken) {
|
||||||
|
private const byte TokenLength = AuthToken.Length;
|
||||||
|
|
||||||
|
public byte[] ToBytes() {
|
||||||
|
Span<byte> result = stackalloc byte[TokenLength + CertificatePublicKey.Length];
|
||||||
|
AuthToken.WriteTo(result[..TokenLength]);
|
||||||
|
CertificatePublicKey.CopyTo(result[TokenLength..]);
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionCommonKey FromBytes(byte[] agentKey) {
|
||||||
|
var authToken = new AuthToken(agentKey[..TokenLength]);
|
||||||
|
var certificatePublicKey = agentKey[TokenLength..];
|
||||||
|
return new ConnectionCommonKey(certificatePublicKey, authToken);
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ public static class PhantomLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ILogger Create(string name1, string name2) {
|
public static ILogger Create(string name1, string name2) {
|
||||||
return Create(name1 + ":" + name2);
|
return Create(ConcatNames(name1, name2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ILogger Create<T>() {
|
public static ILogger Create<T>() {
|
||||||
@ -38,10 +38,18 @@ public static class PhantomLogger {
|
|||||||
return Create(typeof(T).Name, name);
|
return Create(typeof(T).Name, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ILogger Create<T>(string name1, string name2) {
|
||||||
|
return Create(typeof(T).Name, ConcatNames(name1, name2));
|
||||||
|
}
|
||||||
|
|
||||||
public static ILogger Create<T1, T2>() {
|
public static ILogger Create<T1, T2>() {
|
||||||
return Create(typeof(T1).Name, typeof(T2).Name);
|
return Create(typeof(T1).Name, typeof(T2).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ConcatNames(string name1, string name2) {
|
||||||
|
return name1 + ":" + name2;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Dispose() {
|
public static void Dispose() {
|
||||||
Root.Dispose();
|
Root.Dispose();
|
||||||
Base.Dispose();
|
Base.Dispose();
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Common.Logging;
|
||||||
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
|
public static class AgentMessageRegistries {
|
||||||
|
public static MessageRegistry<IMessageToAgentListener> ToAgent { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToAgent)));
|
||||||
|
public static MessageRegistry<IMessageToControllerListener> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
||||||
|
|
||||||
|
public static IMessageDefinitions<IMessageToAgentListener, IMessageToControllerListener> Definitions { get; } = new MessageDefinitions();
|
||||||
|
|
||||||
|
static AgentMessageRegistries() {
|
||||||
|
ToAgent.Add<RegisterAgentSuccessMessage>(0);
|
||||||
|
ToAgent.Add<RegisterAgentFailureMessage>(1);
|
||||||
|
ToAgent.Add<ConfigureInstanceMessage, InstanceActionResult<ConfigureInstanceResult>>(2);
|
||||||
|
ToAgent.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(3);
|
||||||
|
ToAgent.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(4);
|
||||||
|
ToAgent.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(5);
|
||||||
|
ToAgent.Add<ReplyMessage>(127);
|
||||||
|
|
||||||
|
ToController.Add<RegisterAgentMessage>(0);
|
||||||
|
ToController.Add<UnregisterAgentMessage>(1);
|
||||||
|
ToController.Add<AgentIsAliveMessage>(2);
|
||||||
|
ToController.Add<AdvertiseJavaRuntimesMessage>(3);
|
||||||
|
ToController.Add<ReportInstanceStatusMessage>(4);
|
||||||
|
ToController.Add<InstanceOutputMessage>(5);
|
||||||
|
ToController.Add<ReportAgentStatusMessage>(6);
|
||||||
|
ToController.Add<ReportInstanceEventMessage>(7);
|
||||||
|
ToController.Add<ReplyMessage>(127);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToAgentListener, IMessageToControllerListener> {
|
||||||
|
public MessageRegistry<IMessageToAgentListener> ToClient => ToAgent;
|
||||||
|
public MessageRegistry<IMessageToControllerListener> ToServer => ToController;
|
||||||
|
|
||||||
|
public bool IsRegistrationMessage(Type messageType) {
|
||||||
|
return messageType == typeof(RegisterAgentMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMessage<IMessageToAgentListener, NoReply> CreateReplyToServerMessage( uint sequenceId, byte[] serializedReply) {
|
||||||
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMessage<IMessageToControllerListener, NoReply> CreateReplyToClientMessage(uint sequenceId, byte[] serializedReply) {
|
||||||
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.BiDirectional;
|
namespace Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record ReplyMessage(
|
public sealed partial record ReplyMessage(
|
||||||
[property: MemoryPackOrder(0)] uint SequenceId,
|
[property: MemoryPackOrder(0)] uint SequenceId,
|
||||||
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
||||||
) : IMessageToServer, IMessageToAgent {
|
) : IMessageToController, IMessageToAgent, IReply {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleReply(this);
|
return listener.HandleReply(this);
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
public interface IMessageToAgent<TReply> : IMessage<IMessageToAgentListener, TReply> {}
|
public interface IMessageToAgent<TReply> : IMessage<IMessageToAgentListener, TReply> {}
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Messages.BiDirectional;
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
using Phantom.Common.Messages.ToAgent;
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
public interface IMessageToAgentListener {
|
public interface IMessageToAgentListener {
|
||||||
Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message);
|
Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message);
|
@ -0,0 +1,7 @@
|
|||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
|
public interface IMessageToController<TReply> : IMessage<IMessageToControllerListener, TReply> {}
|
||||||
|
|
||||||
|
public interface IMessageToController : IMessageToController<NoReply> {}
|
@ -1,11 +1,10 @@
|
|||||||
using Phantom.Common.Messages.BiDirectional;
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
using Phantom.Common.Messages.ToServer;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
public interface IMessageToServerListener {
|
public interface IMessageToControllerListener {
|
||||||
bool IsDisposed { get; }
|
|
||||||
Task<NoReply> HandleRegisterAgent(RegisterAgentMessage message);
|
Task<NoReply> HandleRegisterAgent(RegisterAgentMessage message);
|
||||||
Task<NoReply> HandleUnregisterAgent(UnregisterAgentMessage message);
|
Task<NoReply> HandleUnregisterAgent(UnregisterAgentMessage message);
|
||||||
Task<NoReply> HandleAgentIsAlive(AgentIsAliveMessage message);
|
Task<NoReply> HandleAgentIsAlive(AgentIsAliveMessage message);
|
@ -2,7 +2,7 @@
|
|||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record ConfigureInstanceMessage(
|
public sealed partial record ConfigureInstanceMessage(
|
@ -1,7 +1,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record LaunchInstanceMessage(
|
public sealed partial record LaunchInstanceMessage(
|
@ -2,7 +2,7 @@
|
|||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterAgentFailureMessage(
|
public sealed partial record RegisterAgentFailureMessage(
|
@ -2,7 +2,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterAgentSuccessMessage(
|
public sealed partial record RegisterAgentSuccessMessage(
|
@ -1,7 +1,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record SendCommandToInstanceMessage(
|
public sealed partial record SendCommandToInstanceMessage(
|
@ -2,7 +2,7 @@
|
|||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record StopInstanceMessage(
|
public sealed partial record StopInstanceMessage(
|
@ -3,13 +3,13 @@ using MemoryPack;
|
|||||||
using Phantom.Common.Data.Java;
|
using Phantom.Common.Data.Java;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record AdvertiseJavaRuntimesMessage(
|
public sealed partial record AdvertiseJavaRuntimesMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<TaggedJavaRuntime> Runtimes
|
[property: MemoryPackOrder(0)] ImmutableArray<TaggedJavaRuntime> Runtimes
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleAdvertiseJavaRuntimes(this);
|
return listener.HandleAdvertiseJavaRuntimes(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record AgentIsAliveMessage : IMessageToController {
|
||||||
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
|
return listener.HandleAgentIsAlive(this);
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,14 @@ using System.Collections.Immutable;
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record InstanceOutputMessage(
|
public sealed partial record InstanceOutputMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleInstanceOutput(this);
|
return listener.HandleInstanceOutput(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,16 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterAgentMessage(
|
public sealed partial record RegisterAgentMessage(
|
||||||
[property: MemoryPackOrder(0)] AgentAuthToken AuthToken,
|
[property: MemoryPackOrder(0)] AuthToken AuthToken,
|
||||||
[property: MemoryPackOrder(1)] AgentInfo AgentInfo
|
[property: MemoryPackOrder(1)] AgentInfo AgentInfo
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleRegisterAgent(this);
|
return listener.HandleRegisterAgent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,14 +2,14 @@
|
|||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record ReportAgentStatusMessage(
|
public sealed partial record ReportAgentStatusMessage(
|
||||||
[property: MemoryPackOrder(0)] int RunningInstanceCount,
|
[property: MemoryPackOrder(0)] int RunningInstanceCount,
|
||||||
[property: MemoryPackOrder(1)] RamAllocationUnits RunningInstanceMemory
|
[property: MemoryPackOrder(1)] RamAllocationUnits RunningInstanceMemory
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleReportAgentStatus(this);
|
return listener.HandleReportAgentStatus(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record ReportInstanceEventMessage(
|
public sealed partial record ReportInstanceEventMessage(
|
||||||
@ -10,8 +10,8 @@ public sealed partial record ReportInstanceEventMessage(
|
|||||||
[property: MemoryPackOrder(1)] DateTime UtcTime,
|
[property: MemoryPackOrder(1)] DateTime UtcTime,
|
||||||
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(3)] IInstanceEvent Event
|
[property: MemoryPackOrder(3)] IInstanceEvent Event
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleReportInstanceEvent(this);
|
return listener.HandleReportInstanceEvent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,14 +2,14 @@
|
|||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record ReportInstanceStatusMessage(
|
public sealed partial record ReportInstanceStatusMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] IInstanceStatus InstanceStatus
|
[property: MemoryPackOrder(1)] IInstanceStatus InstanceStatus
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleReportInstanceStatus(this);
|
return listener.HandleReportInstanceStatus(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record UnregisterAgentMessage(
|
public sealed partial record UnregisterAgentMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid AgentGuid
|
[property: MemoryPackOrder(0)] Guid AgentGuid
|
||||||
) : IMessageToServer {
|
) : IMessageToController {
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
return listener.HandleUnregisterAgent(this);
|
return listener.HandleUnregisterAgent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web.BiDirectional;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record ReplyMessage(
|
||||||
|
[property: MemoryPackOrder(0)] uint SequenceId,
|
||||||
|
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
||||||
|
) : IMessageToController, IMessageToWeb, IReply {
|
||||||
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
|
return listener.HandleReply(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
||||||
|
return listener.HandleReply(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
|
public interface IMessageToController<TReply> : IMessage<IMessageToControllerListener, TReply> {}
|
||||||
|
|
||||||
|
public interface IMessageToController : IMessageToController<NoReply> {}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Common.Messages.Web.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
|
public interface IMessageToControllerListener {
|
||||||
|
Task<NoReply> HandleRegisterWeb(RegisterWebMessage message);
|
||||||
|
Task<CreateOrUpdateAdministratorUserResult> CreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUser message);
|
||||||
|
Task<NoReply> HandleReply(ReplyMessage message);
|
||||||
|
}
|
7
Common/Phantom.Common.Messages.Web/IMessageToWeb.cs
Normal file
7
Common/Phantom.Common.Messages.Web/IMessageToWeb.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
|
public interface IMessageToWeb<TReply> : IMessage<IMessageToWebListener, TReply> {}
|
||||||
|
|
||||||
|
public interface IMessageToWeb : IMessageToWeb<NoReply> {}
|
@ -0,0 +1,8 @@
|
|||||||
|
using Phantom.Common.Messages.Web.BiDirectional;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
|
public interface IMessageToWebListener {
|
||||||
|
Task<NoReply> HandleReply(ReplyMessage message);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
||||||
|
<ProjectReference Include="..\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
||||||
|
<ProjectReference Include="..\Phantom.Common.Data.Web\Phantom.Common.Data.Web.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Utils\Phantom.Utils.Rpc\Phantom.Utils.Rpc.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,14 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record CreateOrUpdateAdministratorUser(
|
||||||
|
[property: MemoryPackOrder(0)] string Username,
|
||||||
|
[property: MemoryPackOrder(1)] string Password
|
||||||
|
) : IMessageToController<CreateOrUpdateAdministratorUserResult> {
|
||||||
|
public Task<CreateOrUpdateAdministratorUserResult> Accept(IMessageToControllerListener listener) {
|
||||||
|
return listener.CreateOrUpdateAdministratorUser(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
|
public sealed partial record RegisterWebMessage(
|
||||||
|
[property: MemoryPackOrder(0)] AuthToken AuthToken
|
||||||
|
) : IMessageToController {
|
||||||
|
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
||||||
|
return listener.HandleRegisterWeb(this);
|
||||||
|
}
|
||||||
|
}
|
38
Common/Phantom.Common.Messages.Web/WebMessageRegistries.cs
Normal file
38
Common/Phantom.Common.Messages.Web/WebMessageRegistries.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Common.Logging;
|
||||||
|
using Phantom.Common.Messages.Web.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
|
public static class WebMessageRegistries {
|
||||||
|
public static MessageRegistry<IMessageToControllerListener> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
||||||
|
public static MessageRegistry<IMessageToWebListener> ToWeb { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToWeb)));
|
||||||
|
|
||||||
|
public static IMessageDefinitions<IMessageToWebListener, IMessageToControllerListener> Definitions { get; } = new MessageDefinitions();
|
||||||
|
|
||||||
|
static WebMessageRegistries() {
|
||||||
|
ToController.Add<CreateOrUpdateAdministratorUser, CreateOrUpdateAdministratorUserResult>(1);
|
||||||
|
ToController.Add<ReplyMessage>(127);
|
||||||
|
|
||||||
|
ToWeb.Add<ReplyMessage>(127);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToWebListener, IMessageToControllerListener> {
|
||||||
|
public MessageRegistry<IMessageToWebListener> ToClient => ToWeb;
|
||||||
|
public MessageRegistry<IMessageToControllerListener> ToServer => ToController;
|
||||||
|
|
||||||
|
public bool IsRegistrationMessage(Type messageType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMessage<IMessageToWebListener, NoReply> CreateReplyToServerMessage( uint sequenceId, byte[] serializedReply) {
|
||||||
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMessage<IMessageToControllerListener, NoReply> CreateReplyToClientMessage(uint sequenceId, byte[] serializedReply) {
|
||||||
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages;
|
|
||||||
|
|
||||||
public interface IMessageToServer<TReply> : IMessage<IMessageToServerListener, TReply> {}
|
|
||||||
|
|
||||||
public interface IMessageToServer : IMessageToServer<NoReply> {}
|
|
@ -1,33 +0,0 @@
|
|||||||
using Phantom.Common.Data.Replies;
|
|
||||||
using Phantom.Common.Logging;
|
|
||||||
using Phantom.Common.Messages.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.ToAgent;
|
|
||||||
using Phantom.Common.Messages.ToServer;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages;
|
|
||||||
|
|
||||||
public static class MessageRegistries {
|
|
||||||
public static MessageRegistry<IMessageToAgentListener> ToAgent { get; } = new (PhantomLogger.Create("MessageRegistry:ToAgent"));
|
|
||||||
public static MessageRegistry<IMessageToServerListener> ToServer { get; } = new (PhantomLogger.Create("MessageRegistry:ToServer"));
|
|
||||||
|
|
||||||
static MessageRegistries() {
|
|
||||||
ToAgent.Add<RegisterAgentSuccessMessage>(0);
|
|
||||||
ToAgent.Add<RegisterAgentFailureMessage>(1);
|
|
||||||
ToAgent.Add<ConfigureInstanceMessage, InstanceActionResult<ConfigureInstanceResult>>(2);
|
|
||||||
ToAgent.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(3);
|
|
||||||
ToAgent.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(4);
|
|
||||||
ToAgent.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(5);
|
|
||||||
ToAgent.Add<ReplyMessage>(127);
|
|
||||||
|
|
||||||
ToServer.Add<RegisterAgentMessage>(0);
|
|
||||||
ToServer.Add<UnregisterAgentMessage>(1);
|
|
||||||
ToServer.Add<AgentIsAliveMessage>(2);
|
|
||||||
ToServer.Add<AdvertiseJavaRuntimesMessage>(3);
|
|
||||||
ToServer.Add<ReportInstanceStatusMessage>(4);
|
|
||||||
ToServer.Add<InstanceOutputMessage>(5);
|
|
||||||
ToServer.Add<ReportAgentStatusMessage>(6);
|
|
||||||
ToServer.Add<ReportInstanceEventMessage>(7);
|
|
||||||
ToServer.Add<ReplyMessage>(127);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using MemoryPack;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.ToServer;
|
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
|
||||||
public sealed partial record AgentIsAliveMessage : IMessageToServer {
|
|
||||||
public Task<NoReply> Accept(IMessageToServerListener listener) {
|
|
||||||
return listener.HandleAgentIsAlive(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Postgres;
|
||||||
|
|
||||||
|
public sealed class ApplicationDbContextFactory : IDbContextProvider {
|
||||||
|
private readonly PooledDbContextFactory<ApplicationDbContext> factory;
|
||||||
|
|
||||||
|
public ApplicationDbContextFactory(string connectionString) {
|
||||||
|
this.factory = new PooledDbContextFactory<ApplicationDbContext>(CreateOptions(connectionString), poolSize: 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationDbContext Eager() {
|
||||||
|
return factory.CreateDbContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILazyDbContext Lazy() {
|
||||||
|
return new LazyDbContext(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DbContextOptions<ApplicationDbContext> CreateOptions(string connectionString) {
|
||||||
|
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
|
||||||
|
builder.UseNpgsql(connectionString, ConfigureOptions);
|
||||||
|
return builder.Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureOptions(NpgsqlDbContextOptionsBuilder options) {
|
||||||
|
options.CommandTimeout(10);
|
||||||
|
options.MigrationsAssembly(typeof(ApplicationDbContextDesignFactory).Assembly.FullName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
namespace Phantom.Controller.Database.Postgres;
|
||||||
|
|
||||||
|
sealed class LazyDbContext : ILazyDbContext {
|
||||||
|
public ApplicationDbContext Ctx => cachedContext ??= contextFactory.Eager();
|
||||||
|
|
||||||
|
private readonly ApplicationDbContextFactory contextFactory;
|
||||||
|
private ApplicationDbContext? cachedContext;
|
||||||
|
|
||||||
|
internal LazyDbContext(ApplicationDbContextFactory contextFactory) {
|
||||||
|
this.contextFactory = contextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync() {
|
||||||
|
return cachedContext?.DisposeAsync() ?? ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,10 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
using Phantom.Controller.Database.Converters;
|
using Phantom.Controller.Database.Converters;
|
||||||
using Phantom.Controller.Database.Entities;
|
using Phantom.Controller.Database.Entities;
|
||||||
using Phantom.Controller.Database.Enums;
|
|
||||||
using Phantom.Controller.Database.Factories;
|
using Phantom.Controller.Database.Factories;
|
||||||
|
|
||||||
namespace Phantom.Controller.Database;
|
namespace Phantom.Controller.Database;
|
||||||
|
25
Controller/Phantom.Controller.Database/DatabaseMigrator.cs
Normal file
25
Controller/Phantom.Controller.Database/DatabaseMigrator.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Phantom.Common.Logging;
|
||||||
|
using Phantom.Utils.Tasks;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database;
|
||||||
|
|
||||||
|
public static class DatabaseMigrator {
|
||||||
|
private static readonly ILogger Logger = PhantomLogger.Create(nameof(DatabaseMigrator));
|
||||||
|
|
||||||
|
public static async Task Run(IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
||||||
|
await using var ctx = dbProvider.Eager();
|
||||||
|
|
||||||
|
Logger.Information("Connecting to database...");
|
||||||
|
|
||||||
|
var retryConnection = new Throttler(TimeSpan.FromSeconds(10));
|
||||||
|
while (!await ctx.Database.CanConnectAsync(cancellationToken)) {
|
||||||
|
Logger.Warning("Cannot connect to database, retrying...");
|
||||||
|
await retryConnection.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Information("Running migrations...");
|
||||||
|
await ctx.Database.MigrateAsync(CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Phantom.Controller.Database;
|
|
||||||
|
|
||||||
public sealed class DatabaseProvider {
|
|
||||||
private readonly IServiceScopeFactory serviceScopeFactory;
|
|
||||||
|
|
||||||
public DatabaseProvider(IServiceScopeFactory serviceScopeFactory) {
|
|
||||||
this.serviceScopeFactory = serviceScopeFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Scope CreateScope() {
|
|
||||||
return new Scope(serviceScopeFactory.CreateScope());
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct Scope : IDisposable {
|
|
||||||
private readonly IServiceScope scope;
|
|
||||||
|
|
||||||
public ApplicationDbContext Ctx { get; }
|
|
||||||
|
|
||||||
internal Scope(IServiceScope scope) {
|
|
||||||
this.scope = scope;
|
|
||||||
this.Ctx = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
scope.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phantom.Controller.Database.Enums;
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
|
||||||
namespace Phantom.Controller.Database.Entities;
|
namespace Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phantom.Controller.Database.Enums;
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
|
|
||||||
namespace Phantom.Controller.Database.Entities;
|
namespace Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
namespace Phantom.Controller.Database.Entities;
|
namespace Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
@ -11,9 +12,13 @@ public sealed class UserEntity {
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string PasswordHash { get; set; }
|
public string PasswordHash { get; set; }
|
||||||
|
|
||||||
public UserEntity(Guid userGuid, string name) {
|
public UserEntity(Guid userGuid, string name, string passwordHash) {
|
||||||
UserGuid = userGuid;
|
UserGuid = userGuid;
|
||||||
Name = name;
|
Name = name;
|
||||||
PasswordHash = null!;
|
PasswordHash = passwordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfo ToUserInfo() {
|
||||||
|
return new UserInfo(UserGuid, Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
namespace Phantom.Controller.Database.Enums;
|
|
||||||
|
|
||||||
public enum EventLogSubjectType {
|
|
||||||
Instance
|
|
||||||
}
|
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Phantom.Controller.Database;
|
||||||
|
|
||||||
|
public interface IDbContextProvider {
|
||||||
|
ApplicationDbContext Eager();
|
||||||
|
ILazyDbContext Lazy();
|
||||||
|
}
|
5
Controller/Phantom.Controller.Database/ILazyDbContext.cs
Normal file
5
Controller/Phantom.Controller.Database/ILazyDbContext.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Phantom.Controller.Database;
|
||||||
|
|
||||||
|
public interface ILazyDbContext : IAsyncDisposable {
|
||||||
|
ApplicationDbContext Ctx { get; }
|
||||||
|
}
|
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Common\Phantom.Common.Data.Web\Phantom.Common.Data.Web.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Common\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
using Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Repositories;
|
||||||
|
|
||||||
|
sealed partial class AuditLogRepository {
|
||||||
|
public void AddUserLoggedInEvent(UserEntity user) {
|
||||||
|
AddItem(AuditLogEventType.UserLoggedIn, user.UserGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUserLoggedOutEvent(Guid userGuid) {
|
||||||
|
AddItem(AuditLogEventType.UserLoggedOut, userGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUserCreatedEvent(UserEntity user) {
|
||||||
|
AddItem(AuditLogEventType.UserCreated, user.UserGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUserPasswordChangedEvent(UserEntity user) {
|
||||||
|
AddItem(AuditLogEventType.UserCreated, user.UserGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUserRolesChangedEvent(UserEntity user, List<string> addedToRoles, List<string> removedFromRoles) {
|
||||||
|
var extra = new Dictionary<string, object?>();
|
||||||
|
|
||||||
|
if (addedToRoles.Count > 0) {
|
||||||
|
extra["addedToRoles"] = addedToRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedFromRoles.Count > 0) {
|
||||||
|
extra["removedFromRoles"] = removedFromRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddItem(AuditLogEventType.UserRolesChanged, user.UserGuid.ToString(), extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddUserDeletedEvent(UserEntity user) {
|
||||||
|
AddItem(AuditLogEventType.UserDeleted, user.UserGuid.ToString(), new Dictionary<string, object?> {
|
||||||
|
{ "username", user.Name }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstanceCreatedEvent(Guid instanceGuid) {
|
||||||
|
AddItem(AuditLogEventType.InstanceCreated, instanceGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstanceEditedEvent(Guid instanceGuid) {
|
||||||
|
AddItem(AuditLogEventType.InstanceEdited, instanceGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstanceLaunchedEvent(Guid instanceGuid) {
|
||||||
|
AddItem(AuditLogEventType.InstanceLaunched, instanceGuid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstanceCommandExecutedEvent(Guid instanceGuid, string command) {
|
||||||
|
AddItem(AuditLogEventType.InstanceCommandExecuted, instanceGuid.ToString(), new Dictionary<string, object?> {
|
||||||
|
{ "command", command }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddInstanceStoppedEvent(Guid instanceGuid, int stopInSeconds) {
|
||||||
|
AddItem(AuditLogEventType.InstanceStopped, instanceGuid.ToString(), new Dictionary<string, object?> {
|
||||||
|
{ "stop_in_seconds", stopInSeconds.ToString() }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
using Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Repositories;
|
||||||
|
|
||||||
|
public sealed partial class AuditLogRepository {
|
||||||
|
private readonly ILazyDbContext db;
|
||||||
|
private readonly Guid? currentUserGuid;
|
||||||
|
|
||||||
|
public AuditLogRepository(ILazyDbContext db, Guid? currentUserGuid) {
|
||||||
|
this.db = db;
|
||||||
|
this.currentUserGuid = currentUserGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddItem(AuditLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
|
||||||
|
db.Ctx.AuditLog.Add(new AuditLogEntity(currentUserGuid, eventType, subjectId, extra));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<AuditLogItem[]> GetItems(int count, CancellationToken cancellationToken) {
|
||||||
|
return db.Ctx
|
||||||
|
.AuditLog
|
||||||
|
.Include(static entity => entity.User)
|
||||||
|
.AsQueryable()
|
||||||
|
.OrderByDescending(static entity => entity.UtcTime)
|
||||||
|
.Take(count)
|
||||||
|
.Select(static entity => new AuditLogItem(entity.UtcTime, entity.UserGuid, entity.User == null ? null : entity.User.Name, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data))
|
||||||
|
.ToArrayAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Controller.Database.Entities;
|
||||||
|
using Phantom.Utils.Collections;
|
||||||
|
using Phantom.Utils.Tasks;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Repositories;
|
||||||
|
|
||||||
|
public sealed class RoleRepository {
|
||||||
|
private const int MaxRoleNameLength = 40;
|
||||||
|
|
||||||
|
private readonly ILazyDbContext db;
|
||||||
|
|
||||||
|
public RoleRepository(ILazyDbContext db) {
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<RoleEntity>> GetAll() {
|
||||||
|
return db.Ctx.Roles.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ImmutableHashSet<string>> GetAllNames() {
|
||||||
|
return db.Ctx.Roles.Select(static role => role.Name).AsAsyncEnumerable().ToImmutableSetAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<RoleEntity?> GetByGuid(Guid guid) {
|
||||||
|
return db.Ctx.Roles.FindAsync(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<RoleEntity, AddRoleError>> Create(string name) {
|
||||||
|
if (string.IsNullOrWhiteSpace(name)) {
|
||||||
|
return AddRoleError.NameIsEmpty;
|
||||||
|
}
|
||||||
|
else if (name.Length > MaxRoleNameLength) {
|
||||||
|
return AddRoleError.NameIsTooLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await db.Ctx.Roles.AnyAsync(role => role.Name == name)) {
|
||||||
|
return AddRoleError.NameAlreadyExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
var role = new RoleEntity(Guid.NewGuid(), name);
|
||||||
|
db.Ctx.Roles.Add(role);
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user